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(); chars.next(); (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 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 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 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 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 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 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 let first_char = chars.next().unwrap();
831 if first_char == '{' || first_char == '}' {
833 let maybe_next_char = chars.next();
834 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 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 let parts: Vec<&str> = arg_part.splitn(2, '!').collect();
878 let arg_part = parts[0];
880
881 let conversion_spec = parts
882 .get(1)
883 .map(|conversion| {
884 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 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 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}