rustpython_format/
format.rs

1use crate::bigint::{BigInt, Sign};
2use itertools::{Itertools, PeekingNext};
3use num_traits::FromPrimitive;
4use num_traits::{cast::ToPrimitive, Signed};
5use rustpython_literal::float;
6use rustpython_literal::format::Case;
7use std::ops::Deref;
8use std::{cmp, str::FromStr};
9
10trait FormatParse {
11    fn parse(text: &str) -> (Option<Self>, &str)
12    where
13        Self: Sized;
14}
15
16#[derive(Debug, Copy, Clone, PartialEq)]
17pub enum FormatConversion {
18    Str,
19    Repr,
20    Ascii,
21    Bytes,
22}
23
24impl FormatParse for FormatConversion {
25    fn parse(text: &str) -> (Option<Self>, &str) {
26        let Some(conversion) = Self::from_string(text) else {
27            return (None, text);
28        };
29        let mut chars = text.chars();
30        chars.next(); // Consume the bang
31        chars.next(); // Consume one r,s,a char
32        (Some(conversion), chars.as_str())
33    }
34}
35
36impl FormatConversion {
37    pub fn from_char(c: char) -> Option<FormatConversion> {
38        match c {
39            's' => Some(FormatConversion::Str),
40            'r' => Some(FormatConversion::Repr),
41            'a' => Some(FormatConversion::Ascii),
42            'b' => Some(FormatConversion::Bytes),
43            _ => None,
44        }
45    }
46
47    fn from_string(text: &str) -> Option<FormatConversion> {
48        let mut chars = text.chars();
49        if chars.next() != Some('!') {
50            return None;
51        }
52
53        FormatConversion::from_char(chars.next()?)
54    }
55}
56
57#[derive(Debug, Copy, Clone, PartialEq)]
58pub enum FormatAlign {
59    Left,
60    Right,
61    AfterSign,
62    Center,
63}
64
65impl FormatAlign {
66    fn from_char(c: char) -> Option<FormatAlign> {
67        match c {
68            '<' => Some(FormatAlign::Left),
69            '>' => Some(FormatAlign::Right),
70            '=' => Some(FormatAlign::AfterSign),
71            '^' => Some(FormatAlign::Center),
72            _ => None,
73        }
74    }
75}
76
77impl FormatParse for FormatAlign {
78    fn parse(text: &str) -> (Option<Self>, &str) {
79        let mut chars = text.chars();
80        if let Some(maybe_align) = chars.next().and_then(Self::from_char) {
81            (Some(maybe_align), chars.as_str())
82        } else {
83            (None, text)
84        }
85    }
86}
87
88#[derive(Debug, Copy, Clone, PartialEq)]
89pub enum FormatSign {
90    Plus,
91    Minus,
92    MinusOrSpace,
93}
94
95impl FormatParse for FormatSign {
96    fn parse(text: &str) -> (Option<Self>, &str) {
97        let mut chars = text.chars();
98        match chars.next() {
99            Some('-') => (Some(Self::Minus), chars.as_str()),
100            Some('+') => (Some(Self::Plus), chars.as_str()),
101            Some(' ') => (Some(Self::MinusOrSpace), chars.as_str()),
102            _ => (None, text),
103        }
104    }
105}
106
107#[derive(Debug, PartialEq)]
108pub enum FormatGrouping {
109    Comma,
110    Underscore,
111}
112
113impl FormatParse for FormatGrouping {
114    fn parse(text: &str) -> (Option<Self>, &str) {
115        let mut chars = text.chars();
116        match chars.next() {
117            Some('_') => (Some(Self::Underscore), chars.as_str()),
118            Some(',') => (Some(Self::Comma), chars.as_str()),
119            _ => (None, text),
120        }
121    }
122}
123
124#[derive(Debug, PartialEq)]
125pub enum FormatType {
126    String,
127    Binary,
128    Character,
129    Decimal,
130    Octal,
131    Number(Case),
132    Hex(Case),
133    Exponent(Case),
134    GeneralFormat(Case),
135    FixedPoint(Case),
136    Percentage,
137}
138
139impl From<&FormatType> for char {
140    fn from(from: &FormatType) -> char {
141        match from {
142            FormatType::String => 's',
143            FormatType::Binary => 'b',
144            FormatType::Character => 'c',
145            FormatType::Decimal => 'd',
146            FormatType::Octal => 'o',
147            FormatType::Number(Case::Lower) => 'n',
148            FormatType::Number(Case::Upper) => 'N',
149            FormatType::Hex(Case::Lower) => 'x',
150            FormatType::Hex(Case::Upper) => 'X',
151            FormatType::Exponent(Case::Lower) => 'e',
152            FormatType::Exponent(Case::Upper) => 'E',
153            FormatType::GeneralFormat(Case::Lower) => 'g',
154            FormatType::GeneralFormat(Case::Upper) => 'G',
155            FormatType::FixedPoint(Case::Lower) => 'f',
156            FormatType::FixedPoint(Case::Upper) => 'F',
157            FormatType::Percentage => '%',
158        }
159    }
160}
161
162impl FormatParse for FormatType {
163    fn parse(text: &str) -> (Option<Self>, &str) {
164        let mut chars = text.chars();
165        match chars.next() {
166            Some('s') => (Some(Self::String), chars.as_str()),
167            Some('b') => (Some(Self::Binary), chars.as_str()),
168            Some('c') => (Some(Self::Character), chars.as_str()),
169            Some('d') => (Some(Self::Decimal), chars.as_str()),
170            Some('o') => (Some(Self::Octal), chars.as_str()),
171            Some('n') => (Some(Self::Number(Case::Lower)), chars.as_str()),
172            Some('N') => (Some(Self::Number(Case::Upper)), chars.as_str()),
173            Some('x') => (Some(Self::Hex(Case::Lower)), chars.as_str()),
174            Some('X') => (Some(Self::Hex(Case::Upper)), chars.as_str()),
175            Some('e') => (Some(Self::Exponent(Case::Lower)), chars.as_str()),
176            Some('E') => (Some(Self::Exponent(Case::Upper)), chars.as_str()),
177            Some('f') => (Some(Self::FixedPoint(Case::Lower)), chars.as_str()),
178            Some('F') => (Some(Self::FixedPoint(Case::Upper)), chars.as_str()),
179            Some('g') => (Some(Self::GeneralFormat(Case::Lower)), chars.as_str()),
180            Some('G') => (Some(Self::GeneralFormat(Case::Upper)), chars.as_str()),
181            Some('%') => (Some(Self::Percentage), chars.as_str()),
182            _ => (None, text),
183        }
184    }
185}
186
187#[derive(Debug, PartialEq)]
188pub struct FormatSpec {
189    conversion: Option<FormatConversion>,
190    fill: Option<char>,
191    align: Option<FormatAlign>,
192    sign: Option<FormatSign>,
193    alternate_form: bool,
194    width: Option<usize>,
195    grouping_option: Option<FormatGrouping>,
196    precision: Option<usize>,
197    format_type: Option<FormatType>,
198}
199
200fn get_num_digits(text: &str) -> usize {
201    for (index, character) in text.char_indices() {
202        if !character.is_ascii_digit() {
203            return index;
204        }
205    }
206    text.len()
207}
208
209fn parse_fill_and_align(text: &str) -> (Option<char>, Option<FormatAlign>, &str) {
210    let char_indices: Vec<(usize, char)> = text.char_indices().take(3).collect();
211    if char_indices.is_empty() {
212        (None, None, text)
213    } else if char_indices.len() == 1 {
214        let (maybe_align, remaining) = FormatAlign::parse(text);
215        (None, maybe_align, remaining)
216    } else {
217        let (maybe_align, remaining) = FormatAlign::parse(&text[char_indices[1].0..]);
218        if maybe_align.is_some() {
219            (Some(char_indices[0].1), maybe_align, remaining)
220        } else {
221            let (only_align, only_align_remaining) = FormatAlign::parse(text);
222            (None, only_align, only_align_remaining)
223        }
224    }
225}
226
227fn parse_number(text: &str) -> Result<(Option<usize>, &str), FormatSpecError> {
228    let num_digits: usize = get_num_digits(text);
229    if num_digits == 0 {
230        return Ok((None, text));
231    }
232    if let Ok(num) = text[..num_digits].parse::<usize>() {
233        Ok((Some(num), &text[num_digits..]))
234    } else {
235        // NOTE: this condition is different from CPython
236        Err(FormatSpecError::DecimalDigitsTooMany)
237    }
238}
239
240fn parse_alternate_form(text: &str) -> (bool, &str) {
241    let mut chars = text.chars();
242    match chars.next() {
243        Some('#') => (true, chars.as_str()),
244        _ => (false, text),
245    }
246}
247
248fn parse_zero(text: &str) -> (bool, &str) {
249    let mut chars = text.chars();
250    match chars.next() {
251        Some('0') => (true, chars.as_str()),
252        _ => (false, text),
253    }
254}
255
256fn parse_precision(text: &str) -> Result<(Option<usize>, &str), FormatSpecError> {
257    let mut chars = text.chars();
258    Ok(match chars.next() {
259        Some('.') => {
260            let (size, remaining) = parse_number(chars.as_str())?;
261            if let Some(size) = size {
262                if size > i32::MAX as usize {
263                    return Err(FormatSpecError::PrecisionTooBig);
264                }
265                (Some(size), remaining)
266            } else {
267                (None, text)
268            }
269        }
270        _ => (None, text),
271    })
272}
273
274impl FormatSpec {
275    pub fn parse(text: &str) -> Result<Self, FormatSpecError> {
276        // get_integer in CPython
277        let (conversion, text) = FormatConversion::parse(text);
278        let (mut fill, mut align, text) = parse_fill_and_align(text);
279        let (sign, text) = FormatSign::parse(text);
280        let (alternate_form, text) = parse_alternate_form(text);
281        let (zero, text) = parse_zero(text);
282        let (width, text) = parse_number(text)?;
283        let (grouping_option, text) = FormatGrouping::parse(text);
284        let (precision, text) = parse_precision(text)?;
285        let (format_type, text) = FormatType::parse(text);
286        if !text.is_empty() {
287            return Err(FormatSpecError::InvalidFormatSpecifier);
288        }
289
290        if zero && fill.is_none() {
291            fill.replace('0');
292            align = align.or(Some(FormatAlign::AfterSign));
293        }
294
295        Ok(FormatSpec {
296            conversion,
297            fill,
298            align,
299            sign,
300            alternate_form,
301            width,
302            grouping_option,
303            precision,
304            format_type,
305        })
306    }
307
308    fn compute_fill_string(fill_char: char, fill_chars_needed: i32) -> String {
309        (0..fill_chars_needed)
310            .map(|_| fill_char)
311            .collect::<String>()
312    }
313
314    fn add_magnitude_separators_for_char(
315        magnitude_str: String,
316        inter: i32,
317        sep: char,
318        disp_digit_cnt: i32,
319    ) -> String {
320        // Don't add separators to the floating decimal point of numbers
321        let mut parts = magnitude_str.splitn(2, '.');
322        let magnitude_int_str = parts.next().unwrap().to_string();
323        let dec_digit_cnt = magnitude_str.len() as i32 - magnitude_int_str.len() as i32;
324        let int_digit_cnt = disp_digit_cnt - dec_digit_cnt;
325        let mut result = FormatSpec::separate_integer(magnitude_int_str, inter, sep, int_digit_cnt);
326        if let Some(part) = parts.next() {
327            result.push_str(&format!(".{part}"))
328        }
329        result
330    }
331
332    fn separate_integer(
333        magnitude_str: String,
334        inter: i32,
335        sep: char,
336        disp_digit_cnt: i32,
337    ) -> String {
338        let magnitude_len = magnitude_str.len() as i32;
339        let offset = (disp_digit_cnt % (inter + 1) == 0) as i32;
340        let disp_digit_cnt = disp_digit_cnt + offset;
341        let pad_cnt = disp_digit_cnt - magnitude_len;
342        let sep_cnt = disp_digit_cnt / (inter + 1);
343        let diff = pad_cnt - sep_cnt;
344        if pad_cnt > 0 && diff > 0 {
345            // separate with 0 padding
346            let padding = "0".repeat(diff as usize);
347            let padded_num = format!("{padding}{magnitude_str}");
348            FormatSpec::insert_separator(padded_num, inter, sep, sep_cnt)
349        } else {
350            // separate without padding
351            let sep_cnt = (magnitude_len - 1) / inter;
352            FormatSpec::insert_separator(magnitude_str, inter, sep, sep_cnt)
353        }
354    }
355
356    fn insert_separator(mut magnitude_str: String, inter: i32, sep: char, sep_cnt: i32) -> String {
357        let magnitude_len = magnitude_str.len() as i32;
358        for i in 1..sep_cnt + 1 {
359            magnitude_str.insert((magnitude_len - inter * i) as usize, sep);
360        }
361        magnitude_str
362    }
363
364    fn validate_format(&self, default_format_type: FormatType) -> Result<(), FormatSpecError> {
365        let format_type = self.format_type.as_ref().unwrap_or(&default_format_type);
366        match (&self.grouping_option, format_type) {
367            (
368                Some(FormatGrouping::Comma),
369                FormatType::String
370                | FormatType::Character
371                | FormatType::Binary
372                | FormatType::Octal
373                | FormatType::Hex(_)
374                | FormatType::Number(_),
375            ) => {
376                let ch = char::from(format_type);
377                Err(FormatSpecError::UnspecifiedFormat(',', ch))
378            }
379            (
380                Some(FormatGrouping::Underscore),
381                FormatType::String | FormatType::Character | FormatType::Number(_),
382            ) => {
383                let ch = char::from(format_type);
384                Err(FormatSpecError::UnspecifiedFormat('_', ch))
385            }
386            _ => Ok(()),
387        }
388    }
389
390    fn get_separator_interval(&self) -> usize {
391        match self.format_type {
392            Some(FormatType::Binary | FormatType::Octal | FormatType::Hex(_)) => 4,
393            Some(FormatType::Decimal | FormatType::Number(_) | FormatType::FixedPoint(_)) => 3,
394            None => 3,
395            _ => panic!("Separators only valid for numbers!"),
396        }
397    }
398
399    fn add_magnitude_separators(&self, magnitude_str: String, prefix: &str) -> String {
400        match &self.grouping_option {
401            Some(fg) => {
402                let sep = match fg {
403                    FormatGrouping::Comma => ',',
404                    FormatGrouping::Underscore => '_',
405                };
406                let inter = self.get_separator_interval().try_into().unwrap();
407                let magnitude_len = magnitude_str.len();
408                let width = self.width.unwrap_or(magnitude_len) as i32 - prefix.len() as i32;
409                let disp_digit_cnt = cmp::max(width, magnitude_len as i32);
410                FormatSpec::add_magnitude_separators_for_char(
411                    magnitude_str,
412                    inter,
413                    sep,
414                    disp_digit_cnt,
415                )
416            }
417            None => magnitude_str,
418        }
419    }
420
421    pub fn format_bool(&self, input: bool) -> Result<String, FormatSpecError> {
422        let x = u8::from(input);
423        match &self.format_type {
424            Some(
425                FormatType::Binary
426                | FormatType::Decimal
427                | FormatType::Octal
428                | FormatType::Number(Case::Lower)
429                | FormatType::Hex(_)
430                | FormatType::GeneralFormat(_)
431                | FormatType::Character,
432            ) => self.format_int(&BigInt::from_u8(x).unwrap()),
433            Some(FormatType::Exponent(_) | FormatType::FixedPoint(_) | FormatType::Percentage) => {
434                self.format_float(x as f64)
435            }
436            None => {
437                let first_letter = (input.to_string().as_bytes()[0] as char).to_uppercase();
438                Ok(first_letter.collect::<String>() + &input.to_string()[1..])
439            }
440            _ => Err(FormatSpecError::InvalidFormatSpecifier),
441        }
442    }
443
444    pub fn format_float(&self, num: f64) -> Result<String, FormatSpecError> {
445        self.validate_format(FormatType::FixedPoint(Case::Lower))?;
446        let precision = self.precision.unwrap_or(6);
447        let magnitude = num.abs();
448        let raw_magnitude_str: Result<String, FormatSpecError> = match &self.format_type {
449            Some(FormatType::FixedPoint(case)) => Ok(float::format_fixed(
450                precision,
451                magnitude,
452                *case,
453                self.alternate_form,
454            )),
455            Some(FormatType::Decimal)
456            | Some(FormatType::Binary)
457            | Some(FormatType::Octal)
458            | Some(FormatType::Hex(_))
459            | Some(FormatType::String)
460            | Some(FormatType::Character)
461            | Some(FormatType::Number(Case::Upper)) => {
462                let ch = char::from(self.format_type.as_ref().unwrap());
463                Err(FormatSpecError::UnknownFormatCode(ch, "float"))
464            }
465            Some(FormatType::GeneralFormat(case)) | Some(FormatType::Number(case)) => {
466                let precision = if precision == 0 { 1 } else { precision };
467                Ok(float::format_general(
468                    precision,
469                    magnitude,
470                    *case,
471                    self.alternate_form,
472                    false,
473                ))
474            }
475            Some(FormatType::Exponent(case)) => Ok(float::format_exponent(
476                precision,
477                magnitude,
478                *case,
479                self.alternate_form,
480            )),
481            Some(FormatType::Percentage) => match magnitude {
482                magnitude if magnitude.is_nan() => Ok("nan%".to_owned()),
483                magnitude if magnitude.is_infinite() => Ok("inf%".to_owned()),
484                _ => {
485                    let result = format!("{:.*}", precision, magnitude * 100.0);
486                    let point = float::decimal_point_or_empty(precision, self.alternate_form);
487                    Ok(format!("{result}{point}%"))
488                }
489            },
490            None => match magnitude {
491                magnitude if magnitude.is_nan() => Ok("nan".to_owned()),
492                magnitude if magnitude.is_infinite() => Ok("inf".to_owned()),
493                _ => match self.precision {
494                    Some(precision) => Ok(float::format_general(
495                        precision,
496                        magnitude,
497                        Case::Lower,
498                        self.alternate_form,
499                        true,
500                    )),
501                    None => Ok(float::to_string(magnitude)),
502                },
503            },
504        };
505        let format_sign = self.sign.unwrap_or(FormatSign::Minus);
506        let sign_str = if num.is_sign_negative() && !num.is_nan() {
507            "-"
508        } else {
509            match format_sign {
510                FormatSign::Plus => "+",
511                FormatSign::Minus => "",
512                FormatSign::MinusOrSpace => " ",
513            }
514        };
515        let magnitude_str = self.add_magnitude_separators(raw_magnitude_str?, sign_str);
516        self.format_sign_and_align(&AsciiStr::new(&magnitude_str), sign_str, FormatAlign::Right)
517    }
518
519    #[inline]
520    fn format_int_radix(&self, magnitude: BigInt, radix: u32) -> Result<String, FormatSpecError> {
521        match self.precision {
522            Some(_) => Err(FormatSpecError::PrecisionNotAllowed),
523            None => Ok(magnitude.to_str_radix(radix)),
524        }
525    }
526
527    pub fn format_int(&self, num: &BigInt) -> Result<String, FormatSpecError> {
528        self.validate_format(FormatType::Decimal)?;
529        let magnitude = num.abs();
530        let prefix = if self.alternate_form {
531            match self.format_type {
532                Some(FormatType::Binary) => "0b",
533                Some(FormatType::Octal) => "0o",
534                Some(FormatType::Hex(Case::Lower)) => "0x",
535                Some(FormatType::Hex(Case::Upper)) => "0X",
536                _ => "",
537            }
538        } else {
539            ""
540        };
541        let raw_magnitude_str = match self.format_type {
542            Some(FormatType::Binary) => self.format_int_radix(magnitude, 2),
543            Some(FormatType::Decimal) => self.format_int_radix(magnitude, 10),
544            Some(FormatType::Octal) => self.format_int_radix(magnitude, 8),
545            Some(FormatType::Hex(Case::Lower)) => self.format_int_radix(magnitude, 16),
546            Some(FormatType::Hex(Case::Upper)) => match self.precision {
547                Some(_) => Err(FormatSpecError::PrecisionNotAllowed),
548                None => {
549                    let mut result = magnitude.to_str_radix(16);
550                    result.make_ascii_uppercase();
551                    Ok(result)
552                }
553            },
554            Some(FormatType::Number(Case::Lower)) => self.format_int_radix(magnitude, 10),
555            Some(FormatType::Number(Case::Upper)) => {
556                Err(FormatSpecError::UnknownFormatCode('N', "int"))
557            }
558            Some(FormatType::String) => Err(FormatSpecError::UnknownFormatCode('s', "int")),
559            Some(FormatType::Character) => match (self.sign, self.alternate_form) {
560                (Some(_), _) => Err(FormatSpecError::NotAllowed("Sign")),
561                (_, true) => Err(FormatSpecError::NotAllowed("Alternate form (#)")),
562                (_, _) => match num.to_u32() {
563                    Some(n) if n <= 0x10ffff => Ok(std::char::from_u32(n).unwrap().to_string()),
564                    Some(_) | None => Err(FormatSpecError::CodeNotInRange),
565                },
566            },
567            Some(FormatType::GeneralFormat(_))
568            | Some(FormatType::FixedPoint(_))
569            | Some(FormatType::Exponent(_))
570            | Some(FormatType::Percentage) => match num.to_f64() {
571                Some(float) => return self.format_float(float),
572                _ => Err(FormatSpecError::UnableToConvert),
573            },
574            None => self.format_int_radix(magnitude, 10),
575        }?;
576        let format_sign = self.sign.unwrap_or(FormatSign::Minus);
577        let sign_str = match num.sign() {
578            Sign::Minus => "-",
579            _ => match format_sign {
580                FormatSign::Plus => "+",
581                FormatSign::Minus => "",
582                FormatSign::MinusOrSpace => " ",
583            },
584        };
585        let sign_prefix = format!("{sign_str}{prefix}");
586        let magnitude_str = self.add_magnitude_separators(raw_magnitude_str, &sign_prefix);
587        self.format_sign_and_align(
588            &AsciiStr::new(&magnitude_str),
589            &sign_prefix,
590            FormatAlign::Right,
591        )
592    }
593
594    pub fn format_string<T>(&self, s: &T) -> Result<String, FormatSpecError>
595    where
596        T: CharLen + Deref<Target = str>,
597    {
598        self.validate_format(FormatType::String)?;
599        match self.format_type {
600            Some(FormatType::String) | None => self
601                .format_sign_and_align(s, "", FormatAlign::Left)
602                .map(|mut value| {
603                    if let Some(precision) = self.precision {
604                        value.truncate(precision);
605                    }
606                    value
607                }),
608            _ => {
609                let ch = char::from(self.format_type.as_ref().unwrap());
610                Err(FormatSpecError::UnknownFormatCode(ch, "str"))
611            }
612        }
613    }
614
615    fn format_sign_and_align<T>(
616        &self,
617        magnitude_str: &T,
618        sign_str: &str,
619        default_align: FormatAlign,
620    ) -> Result<String, FormatSpecError>
621    where
622        T: CharLen + Deref<Target = str>,
623    {
624        let align = self.align.unwrap_or(default_align);
625
626        let num_chars = magnitude_str.char_len();
627        let fill_char = self.fill.unwrap_or(' ');
628        let fill_chars_needed: i32 = self.width.map_or(0, |w| {
629            cmp::max(0, (w as i32) - (num_chars as i32) - (sign_str.len() as i32))
630        });
631
632        let magnitude_str = magnitude_str.deref();
633        Ok(match align {
634            FormatAlign::Left => format!(
635                "{}{}{}",
636                sign_str,
637                magnitude_str,
638                FormatSpec::compute_fill_string(fill_char, fill_chars_needed)
639            ),
640            FormatAlign::Right => format!(
641                "{}{}{}",
642                FormatSpec::compute_fill_string(fill_char, fill_chars_needed),
643                sign_str,
644                magnitude_str
645            ),
646            FormatAlign::AfterSign => format!(
647                "{}{}{}",
648                sign_str,
649                FormatSpec::compute_fill_string(fill_char, fill_chars_needed),
650                magnitude_str
651            ),
652            FormatAlign::Center => {
653                let left_fill_chars_needed = fill_chars_needed / 2;
654                let right_fill_chars_needed = fill_chars_needed - left_fill_chars_needed;
655                let left_fill_string =
656                    FormatSpec::compute_fill_string(fill_char, left_fill_chars_needed);
657                let right_fill_string =
658                    FormatSpec::compute_fill_string(fill_char, right_fill_chars_needed);
659                format!("{left_fill_string}{sign_str}{magnitude_str}{right_fill_string}")
660            }
661        })
662    }
663}
664
665pub trait CharLen {
666    /// Returns the number of characters in the text
667    fn char_len(&self) -> usize;
668}
669
670struct AsciiStr<'a> {
671    inner: &'a str,
672}
673
674impl<'a> AsciiStr<'a> {
675    fn new(inner: &'a str) -> Self {
676        Self { inner }
677    }
678}
679
680impl CharLen for AsciiStr<'_> {
681    fn char_len(&self) -> usize {
682        self.inner.len()
683    }
684}
685
686impl Deref for AsciiStr<'_> {
687    type Target = str;
688    fn deref(&self) -> &Self::Target {
689        self.inner
690    }
691}
692
693#[derive(Debug, PartialEq)]
694pub enum FormatSpecError {
695    DecimalDigitsTooMany,
696    PrecisionTooBig,
697    InvalidFormatSpecifier,
698    UnspecifiedFormat(char, char),
699    UnknownFormatCode(char, &'static str),
700    PrecisionNotAllowed,
701    NotAllowed(&'static str),
702    UnableToConvert,
703    CodeNotInRange,
704    NotImplemented(char, &'static str),
705}
706
707#[derive(Debug, PartialEq)]
708pub enum FormatParseError {
709    UnmatchedBracket,
710    MissingStartBracket,
711    UnescapedStartBracketInLiteral,
712    InvalidFormatSpecifier,
713    UnknownConversion,
714    EmptyAttribute,
715    MissingRightBracket,
716    InvalidCharacterAfterRightBracket,
717}
718
719impl FromStr for FormatSpec {
720    type Err = FormatSpecError;
721    fn from_str(s: &str) -> Result<Self, Self::Err> {
722        FormatSpec::parse(s)
723    }
724}
725
726#[derive(Debug, PartialEq)]
727pub enum FieldNamePart {
728    Attribute(String),
729    Index(usize),
730    StringIndex(String),
731}
732
733impl FieldNamePart {
734    fn parse_part(
735        chars: &mut impl PeekingNext<Item = char>,
736    ) -> Result<Option<FieldNamePart>, FormatParseError> {
737        chars
738            .next()
739            .map(|ch| match ch {
740                '.' => {
741                    let mut attribute = String::new();
742                    for ch in chars.peeking_take_while(|ch| *ch != '.' && *ch != '[') {
743                        attribute.push(ch);
744                    }
745                    if attribute.is_empty() {
746                        Err(FormatParseError::EmptyAttribute)
747                    } else {
748                        Ok(FieldNamePart::Attribute(attribute))
749                    }
750                }
751                '[' => {
752                    let mut index = String::new();
753                    for ch in chars {
754                        if ch == ']' {
755                            return if index.is_empty() {
756                                Err(FormatParseError::EmptyAttribute)
757                            } else if let Ok(index) = index.parse::<usize>() {
758                                Ok(FieldNamePart::Index(index))
759                            } else {
760                                Ok(FieldNamePart::StringIndex(index))
761                            };
762                        }
763                        index.push(ch);
764                    }
765                    Err(FormatParseError::MissingRightBracket)
766                }
767                _ => Err(FormatParseError::InvalidCharacterAfterRightBracket),
768            })
769            .transpose()
770    }
771}
772
773#[derive(Debug, PartialEq)]
774pub enum FieldType {
775    Auto,
776    Index(usize),
777    Keyword(String),
778}
779
780#[derive(Debug, PartialEq)]
781pub struct FieldName {
782    pub field_type: FieldType,
783    pub parts: Vec<FieldNamePart>,
784}
785
786impl FieldName {
787    pub fn parse(text: &str) -> Result<FieldName, FormatParseError> {
788        let mut chars = text.chars().peekable();
789        let mut first = String::new();
790        for ch in chars.peeking_take_while(|ch| *ch != '.' && *ch != '[') {
791            first.push(ch);
792        }
793
794        let field_type = if first.is_empty() {
795            FieldType::Auto
796        } else if let Ok(index) = first.parse::<usize>() {
797            FieldType::Index(index)
798        } else {
799            FieldType::Keyword(first)
800        };
801
802        let mut parts = Vec::new();
803        while let Some(part) = FieldNamePart::parse_part(&mut chars)? {
804            parts.push(part)
805        }
806
807        Ok(FieldName { field_type, parts })
808    }
809}
810
811#[derive(Debug, PartialEq)]
812pub enum FormatPart {
813    Field {
814        field_name: String,
815        conversion_spec: Option<char>,
816        format_spec: String,
817    },
818    Literal(String),
819}
820
821#[derive(Debug, PartialEq)]
822pub struct FormatString {
823    pub format_parts: Vec<FormatPart>,
824}
825
826impl FormatString {
827    fn parse_literal_single(text: &str) -> Result<(char, &str), FormatParseError> {
828        let mut chars = text.chars();
829        // This should never be called with an empty str
830        let first_char = chars.next().unwrap();
831        // isn't this detectable only with bytes operation?
832        if first_char == '{' || first_char == '}' {
833            let maybe_next_char = chars.next();
834            // if we see a bracket, it has to be escaped by doubling up to be in a literal
835            return if maybe_next_char.is_none() || maybe_next_char.unwrap() != first_char {
836                Err(FormatParseError::UnescapedStartBracketInLiteral)
837            } else {
838                Ok((first_char, chars.as_str()))
839            };
840        }
841        Ok((first_char, chars.as_str()))
842    }
843
844    fn parse_literal(text: &str) -> Result<(FormatPart, &str), FormatParseError> {
845        let mut cur_text = text;
846        let mut result_string = String::new();
847        while !cur_text.is_empty() {
848            match FormatString::parse_literal_single(cur_text) {
849                Ok((next_char, remaining)) => {
850                    result_string.push(next_char);
851                    cur_text = remaining;
852                }
853                Err(err) => {
854                    return if !result_string.is_empty() {
855                        Ok((FormatPart::Literal(result_string), cur_text))
856                    } else {
857                        Err(err)
858                    };
859                }
860            }
861        }
862        Ok((FormatPart::Literal(result_string), ""))
863    }
864
865    fn parse_part_in_brackets(text: &str) -> Result<FormatPart, FormatParseError> {
866        let parts: Vec<&str> = text.splitn(2, ':').collect();
867        // before the comma is a keyword or arg index, after the comma is maybe a spec.
868        let arg_part = parts[0];
869
870        let format_spec = if parts.len() > 1 {
871            parts[1].to_owned()
872        } else {
873            String::new()
874        };
875
876        // On parts[0] can still be the conversion (!r, !s, !a)
877        let parts: Vec<&str> = arg_part.splitn(2, '!').collect();
878        // before the bang is a keyword or arg index, after the comma is maybe a conversion spec.
879        let arg_part = parts[0];
880
881        let conversion_spec = parts
882            .get(1)
883            .map(|conversion| {
884                // conversions are only every one character
885                conversion
886                    .chars()
887                    .exactly_one()
888                    .map_err(|_| FormatParseError::UnknownConversion)
889            })
890            .transpose()?;
891
892        Ok(FormatPart::Field {
893            field_name: arg_part.to_owned(),
894            conversion_spec,
895            format_spec,
896        })
897    }
898
899    fn parse_spec(text: &str) -> Result<(FormatPart, &str), FormatParseError> {
900        let mut nested = false;
901        let mut end_bracket_pos = None;
902        let mut left = String::new();
903
904        // There may be one layer nesting brackets in spec
905        for (idx, c) in text.char_indices() {
906            if idx == 0 {
907                if c != '{' {
908                    return Err(FormatParseError::MissingStartBracket);
909                }
910            } else if c == '{' {
911                if nested {
912                    return Err(FormatParseError::InvalidFormatSpecifier);
913                } else {
914                    nested = true;
915                    left.push(c);
916                    continue;
917                }
918            } else if c == '}' {
919                if nested {
920                    nested = false;
921                    left.push(c);
922                    continue;
923                } else {
924                    end_bracket_pos = Some(idx);
925                    break;
926                }
927            } else {
928                left.push(c);
929            }
930        }
931        if let Some(pos) = end_bracket_pos {
932            let (_, right) = text.split_at(pos);
933            let format_part = FormatString::parse_part_in_brackets(&left)?;
934            Ok((format_part, &right[1..]))
935        } else {
936            Err(FormatParseError::UnmatchedBracket)
937        }
938    }
939}
940
941pub trait FromTemplate<'a>: Sized {
942    type Err;
943    fn from_str(s: &'a str) -> Result<Self, Self::Err>;
944}
945
946impl<'a> FromTemplate<'a> for FormatString {
947    type Err = FormatParseError;
948
949    fn from_str(text: &'a str) -> Result<Self, Self::Err> {
950        let mut cur_text: &str = text;
951        let mut parts: Vec<FormatPart> = Vec::new();
952        while !cur_text.is_empty() {
953            // Try to parse both literals and bracketed format parts until we
954            // run out of text
955            cur_text = FormatString::parse_literal(cur_text)
956                .or_else(|_| FormatString::parse_spec(cur_text))
957                .map(|(part, new_text)| {
958                    parts.push(part);
959                    new_text
960                })?;
961        }
962        Ok(FormatString {
963            format_parts: parts,
964        })
965    }
966}
967
968#[cfg(test)]
969mod tests {
970    use super::*;
971
972    #[test]
973    fn test_fill_and_align() {
974        assert_eq!(
975            parse_fill_and_align(" <"),
976            (Some(' '), Some(FormatAlign::Left), "")
977        );
978        assert_eq!(
979            parse_fill_and_align(" <22"),
980            (Some(' '), Some(FormatAlign::Left), "22")
981        );
982        assert_eq!(
983            parse_fill_and_align("<22"),
984            (None, Some(FormatAlign::Left), "22")
985        );
986        assert_eq!(
987            parse_fill_and_align(" ^^"),
988            (Some(' '), Some(FormatAlign::Center), "^")
989        );
990        assert_eq!(
991            parse_fill_and_align("==="),
992            (Some('='), Some(FormatAlign::AfterSign), "=")
993        );
994    }
995
996    #[test]
997    fn test_width_only() {
998        let expected = Ok(FormatSpec {
999            conversion: None,
1000            fill: None,
1001            align: None,
1002            sign: None,
1003            alternate_form: false,
1004            width: Some(33),
1005            grouping_option: None,
1006            precision: None,
1007            format_type: None,
1008        });
1009        assert_eq!(FormatSpec::parse("33"), expected);
1010    }
1011
1012    #[test]
1013    fn test_fill_and_width() {
1014        let expected = Ok(FormatSpec {
1015            conversion: None,
1016            fill: Some('<'),
1017            align: Some(FormatAlign::Right),
1018            sign: None,
1019            alternate_form: false,
1020            width: Some(33),
1021            grouping_option: None,
1022            precision: None,
1023            format_type: None,
1024        });
1025        assert_eq!(FormatSpec::parse("<>33"), expected);
1026    }
1027
1028    #[test]
1029    fn test_all() {
1030        let expected = Ok(FormatSpec {
1031            conversion: None,
1032            fill: Some('<'),
1033            align: Some(FormatAlign::Right),
1034            sign: Some(FormatSign::Minus),
1035            alternate_form: true,
1036            width: Some(23),
1037            grouping_option: Some(FormatGrouping::Comma),
1038            precision: Some(11),
1039            format_type: Some(FormatType::Binary),
1040        });
1041        assert_eq!(FormatSpec::parse("<>-#23,.11b"), expected);
1042    }
1043
1044    fn format_bool(text: &str, value: bool) -> Result<String, FormatSpecError> {
1045        FormatSpec::parse(text).and_then(|spec| spec.format_bool(value))
1046    }
1047
1048    #[test]
1049    fn test_format_bool() {
1050        assert_eq!(format_bool("b", true), Ok("1".to_owned()));
1051        assert_eq!(format_bool("b", false), Ok("0".to_owned()));
1052        assert_eq!(format_bool("d", true), Ok("1".to_owned()));
1053        assert_eq!(format_bool("d", false), Ok("0".to_owned()));
1054        assert_eq!(format_bool("o", true), Ok("1".to_owned()));
1055        assert_eq!(format_bool("o", false), Ok("0".to_owned()));
1056        assert_eq!(format_bool("n", true), Ok("1".to_owned()));
1057        assert_eq!(format_bool("n", false), Ok("0".to_owned()));
1058        assert_eq!(format_bool("x", true), Ok("1".to_owned()));
1059        assert_eq!(format_bool("x", false), Ok("0".to_owned()));
1060        assert_eq!(format_bool("X", true), Ok("1".to_owned()));
1061        assert_eq!(format_bool("X", false), Ok("0".to_owned()));
1062        assert_eq!(format_bool("g", true), Ok("1".to_owned()));
1063        assert_eq!(format_bool("g", false), Ok("0".to_owned()));
1064        assert_eq!(format_bool("G", true), Ok("1".to_owned()));
1065        assert_eq!(format_bool("G", false), Ok("0".to_owned()));
1066        assert_eq!(format_bool("c", true), Ok("\x01".to_owned()));
1067        assert_eq!(format_bool("c", false), Ok("\x00".to_owned()));
1068        assert_eq!(format_bool("e", true), Ok("1.000000e+00".to_owned()));
1069        assert_eq!(format_bool("e", false), Ok("0.000000e+00".to_owned()));
1070        assert_eq!(format_bool("E", true), Ok("1.000000E+00".to_owned()));
1071        assert_eq!(format_bool("E", false), Ok("0.000000E+00".to_owned()));
1072        assert_eq!(format_bool("f", true), Ok("1.000000".to_owned()));
1073        assert_eq!(format_bool("f", false), Ok("0.000000".to_owned()));
1074        assert_eq!(format_bool("F", true), Ok("1.000000".to_owned()));
1075        assert_eq!(format_bool("F", false), Ok("0.000000".to_owned()));
1076        assert_eq!(format_bool("%", true), Ok("100.000000%".to_owned()));
1077        assert_eq!(format_bool("%", false), Ok("0.000000%".to_owned()));
1078    }
1079
1080    #[test]
1081    fn test_format_int() {
1082        assert_eq!(
1083            FormatSpec::parse("d")
1084                .unwrap()
1085                .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
1086            Ok("16".to_owned())
1087        );
1088        assert_eq!(
1089            FormatSpec::parse("x")
1090                .unwrap()
1091                .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
1092            Ok("10".to_owned())
1093        );
1094        assert_eq!(
1095            FormatSpec::parse("b")
1096                .unwrap()
1097                .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
1098            Ok("10000".to_owned())
1099        );
1100        assert_eq!(
1101            FormatSpec::parse("o")
1102                .unwrap()
1103                .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
1104            Ok("20".to_owned())
1105        );
1106        assert_eq!(
1107            FormatSpec::parse("+d")
1108                .unwrap()
1109                .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
1110            Ok("+16".to_owned())
1111        );
1112        assert_eq!(
1113            FormatSpec::parse("^ 5d")
1114                .unwrap()
1115                .format_int(&BigInt::from_bytes_be(Sign::Minus, b"\x10")),
1116            Ok(" -16 ".to_owned())
1117        );
1118        assert_eq!(
1119            FormatSpec::parse("0>+#10x")
1120                .unwrap()
1121                .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
1122            Ok("00000+0x10".to_owned())
1123        );
1124    }
1125
1126    #[test]
1127    fn test_format_int_sep() {
1128        let spec = FormatSpec::parse(",").expect("");
1129        assert_eq!(spec.grouping_option, Some(FormatGrouping::Comma));
1130        assert_eq!(
1131            spec.format_int(&BigInt::from_str("1234567890123456789012345678").unwrap()),
1132            Ok("1,234,567,890,123,456,789,012,345,678".to_owned())
1133        );
1134    }
1135
1136    #[test]
1137    fn test_format_parse() {
1138        let expected = Ok(FormatString {
1139            format_parts: vec![
1140                FormatPart::Literal("abcd".to_owned()),
1141                FormatPart::Field {
1142                    field_name: "1".to_owned(),
1143                    conversion_spec: None,
1144                    format_spec: String::new(),
1145                },
1146                FormatPart::Literal(":".to_owned()),
1147                FormatPart::Field {
1148                    field_name: "key".to_owned(),
1149                    conversion_spec: None,
1150                    format_spec: String::new(),
1151                },
1152            ],
1153        });
1154
1155        assert_eq!(FormatString::from_str("abcd{1}:{key}"), expected);
1156    }
1157
1158    #[test]
1159    fn test_format_parse_multi_byte_char() {
1160        assert!(FormatString::from_str("{a:%ЫйЯЧ}").is_ok());
1161    }
1162
1163    #[test]
1164    fn test_format_parse_fail() {
1165        assert_eq!(
1166            FormatString::from_str("{s"),
1167            Err(FormatParseError::UnmatchedBracket)
1168        );
1169    }
1170
1171    #[test]
1172    fn test_format_parse_escape() {
1173        let expected = Ok(FormatString {
1174            format_parts: vec![
1175                FormatPart::Literal("{".to_owned()),
1176                FormatPart::Field {
1177                    field_name: "key".to_owned(),
1178                    conversion_spec: None,
1179                    format_spec: String::new(),
1180                },
1181                FormatPart::Literal("}ddfe".to_owned()),
1182            ],
1183        });
1184
1185        assert_eq!(FormatString::from_str("{{{key}}}ddfe"), expected);
1186    }
1187
1188    #[test]
1189    fn test_format_invalid_specification() {
1190        assert_eq!(
1191            FormatSpec::parse("%3"),
1192            Err(FormatSpecError::InvalidFormatSpecifier)
1193        );
1194        assert_eq!(
1195            FormatSpec::parse(".2fa"),
1196            Err(FormatSpecError::InvalidFormatSpecifier)
1197        );
1198        assert_eq!(
1199            FormatSpec::parse("ds"),
1200            Err(FormatSpecError::InvalidFormatSpecifier)
1201        );
1202        assert_eq!(
1203            FormatSpec::parse("x+"),
1204            Err(FormatSpecError::InvalidFormatSpecifier)
1205        );
1206        assert_eq!(
1207            FormatSpec::parse("b4"),
1208            Err(FormatSpecError::InvalidFormatSpecifier)
1209        );
1210        assert_eq!(
1211            FormatSpec::parse("o!"),
1212            Err(FormatSpecError::InvalidFormatSpecifier)
1213        );
1214        assert_eq!(
1215            FormatSpec::parse("d "),
1216            Err(FormatSpecError::InvalidFormatSpecifier)
1217        );
1218    }
1219
1220    #[test]
1221    fn test_parse_field_name() {
1222        assert_eq!(
1223            FieldName::parse(""),
1224            Ok(FieldName {
1225                field_type: FieldType::Auto,
1226                parts: Vec::new(),
1227            })
1228        );
1229        assert_eq!(
1230            FieldName::parse("0"),
1231            Ok(FieldName {
1232                field_type: FieldType::Index(0),
1233                parts: Vec::new(),
1234            })
1235        );
1236        assert_eq!(
1237            FieldName::parse("key"),
1238            Ok(FieldName {
1239                field_type: FieldType::Keyword("key".to_owned()),
1240                parts: Vec::new(),
1241            })
1242        );
1243        assert_eq!(
1244            FieldName::parse("key.attr[0][string]"),
1245            Ok(FieldName {
1246                field_type: FieldType::Keyword("key".to_owned()),
1247                parts: vec![
1248                    FieldNamePart::Attribute("attr".to_owned()),
1249                    FieldNamePart::Index(0),
1250                    FieldNamePart::StringIndex("string".to_owned())
1251                ],
1252            })
1253        );
1254        assert_eq!(
1255            FieldName::parse("key.."),
1256            Err(FormatParseError::EmptyAttribute)
1257        );
1258        assert_eq!(
1259            FieldName::parse("key[]"),
1260            Err(FormatParseError::EmptyAttribute)
1261        );
1262        assert_eq!(
1263            FieldName::parse("key["),
1264            Err(FormatParseError::MissingRightBracket)
1265        );
1266        assert_eq!(
1267            FieldName::parse("key[0]after"),
1268            Err(FormatParseError::InvalidCharacterAfterRightBracket)
1269        );
1270    }
1271}