1use core::ops::Deref;
3use core::{cmp, str::FromStr};
4use itertools::{Itertools, PeekingNext};
5use malachite_base::num::basic::floats::PrimitiveFloat;
6use malachite_bigint::{BigInt, Sign};
7use num_complex::Complex64;
8use num_traits::FromPrimitive;
9use num_traits::{Signed, cast::ToPrimitive};
10use rustpython_literal::float;
11use rustpython_literal::format::Case;
12
13use crate::wtf8::{CodePoint, Wtf8, Wtf8Buf};
14
15#[derive(Clone, Debug)]
19pub struct LocaleInfo {
20 pub thousands_sep: String,
21 pub decimal_point: String,
22 pub grouping: Vec<u8>,
26}
27
28trait FormatParse {
29 fn parse(text: &Wtf8) -> (Option<Self>, &Wtf8)
30 where
31 Self: Sized;
32}
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq)]
35#[repr(u8)]
36pub enum FormatConversion {
37 Str = b's',
38 Repr = b'r',
39 Ascii = b'b',
40}
41
42impl FormatConversion {
43 #[must_use]
44 pub fn from_char(c: CodePoint) -> Option<Self> {
45 match c.to_char_lossy() {
46 's' => Some(Self::Str),
47 'r' => Some(Self::Repr),
48 'a' => Some(Self::Ascii),
49 _ => None,
50 }
51 }
52}
53
54#[derive(Debug, Copy, Clone, PartialEq, Eq)]
55pub enum FormatAlign {
56 Left,
57 Right,
58 AfterSign,
59 Center,
60}
61
62impl FormatAlign {
63 fn from_char(c: CodePoint) -> Option<Self> {
64 match c.to_char_lossy() {
65 '<' => Some(Self::Left),
66 '>' => Some(Self::Right),
67 '=' => Some(Self::AfterSign),
68 '^' => Some(Self::Center),
69 _ => None,
70 }
71 }
72}
73
74impl FormatParse for FormatAlign {
75 fn parse(text: &Wtf8) -> (Option<Self>, &Wtf8) {
76 let mut chars = text.code_points();
77 if let Some(maybe_align) = chars.next().and_then(Self::from_char) {
78 (Some(maybe_align), chars.as_wtf8())
79 } else {
80 (None, text)
81 }
82 }
83}
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq)]
86pub enum FormatSign {
87 Plus,
88 Minus,
89 MinusOrSpace,
90}
91
92impl FormatParse for FormatSign {
93 fn parse(text: &Wtf8) -> (Option<Self>, &Wtf8) {
94 let mut chars = text.code_points();
95 match chars.next().and_then(CodePoint::to_char) {
96 Some('-') => (Some(Self::Minus), chars.as_wtf8()),
97 Some('+') => (Some(Self::Plus), chars.as_wtf8()),
98 Some(' ') => (Some(Self::MinusOrSpace), chars.as_wtf8()),
99 _ => (None, text),
100 }
101 }
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub enum FormatGrouping {
106 Comma,
107 Underscore,
108}
109
110impl FormatParse for FormatGrouping {
111 fn parse(text: &Wtf8) -> (Option<Self>, &Wtf8) {
112 let mut chars = text.code_points();
113 match chars.next().and_then(CodePoint::to_char) {
114 Some('_') => (Some(Self::Underscore), chars.as_wtf8()),
115 Some(',') => (Some(Self::Comma), chars.as_wtf8()),
116 _ => (None, text),
117 }
118 }
119}
120
121impl From<FormatGrouping> for char {
122 fn from(fg: FormatGrouping) -> Self {
123 match fg {
124 FormatGrouping::Comma => ',',
125 FormatGrouping::Underscore => '_',
126 }
127 }
128}
129
130impl From<&FormatGrouping> for char {
131 fn from(fg: &FormatGrouping) -> Self {
132 Self::from(*fg)
133 }
134}
135
136#[derive(Clone, Copy, Debug, PartialEq, Eq)]
137pub enum FormatType {
138 String,
139 Binary,
140 Character,
141 Decimal,
142 Octal,
143 Number(Case),
144 Hex(Case),
145 Exponent(Case),
146 GeneralFormat(Case),
147 FixedPoint(Case),
148 Percentage,
149 Unknown(char),
150}
151
152impl From<&FormatType> for char {
153 fn from(from: &FormatType) -> Self {
154 match from {
155 FormatType::String => 's',
156 FormatType::Binary => 'b',
157 FormatType::Character => 'c',
158 FormatType::Decimal => 'd',
159 FormatType::Octal => 'o',
160 FormatType::Number(Case::Lower) => 'n',
161 FormatType::Number(Case::Upper) => 'N',
162 FormatType::Hex(Case::Lower) => 'x',
163 FormatType::Hex(Case::Upper) => 'X',
164 FormatType::Exponent(Case::Lower) => 'e',
165 FormatType::Exponent(Case::Upper) => 'E',
166 FormatType::GeneralFormat(Case::Lower) => 'g',
167 FormatType::GeneralFormat(Case::Upper) => 'G',
168 FormatType::FixedPoint(Case::Lower) => 'f',
169 FormatType::FixedPoint(Case::Upper) => 'F',
170 FormatType::Percentage => '%',
171 FormatType::Unknown(c) => *c,
172 }
173 }
174}
175
176impl FormatParse for FormatType {
177 fn parse(text: &Wtf8) -> (Option<Self>, &Wtf8) {
178 let mut chars = text.code_points();
179 match chars.next().and_then(CodePoint::to_char) {
180 Some('s') => (Some(Self::String), chars.as_wtf8()),
181 Some('b') => (Some(Self::Binary), chars.as_wtf8()),
182 Some('c') => (Some(Self::Character), chars.as_wtf8()),
183 Some('d') => (Some(Self::Decimal), chars.as_wtf8()),
184 Some('o') => (Some(Self::Octal), chars.as_wtf8()),
185 Some('n') => (Some(Self::Number(Case::Lower)), chars.as_wtf8()),
186 Some('N') => (Some(Self::Number(Case::Upper)), chars.as_wtf8()),
187 Some('x') => (Some(Self::Hex(Case::Lower)), chars.as_wtf8()),
188 Some('X') => (Some(Self::Hex(Case::Upper)), chars.as_wtf8()),
189 Some('e') => (Some(Self::Exponent(Case::Lower)), chars.as_wtf8()),
190 Some('E') => (Some(Self::Exponent(Case::Upper)), chars.as_wtf8()),
191 Some('f') => (Some(Self::FixedPoint(Case::Lower)), chars.as_wtf8()),
192 Some('F') => (Some(Self::FixedPoint(Case::Upper)), chars.as_wtf8()),
193 Some('g') => (Some(Self::GeneralFormat(Case::Lower)), chars.as_wtf8()),
194 Some('G') => (Some(Self::GeneralFormat(Case::Upper)), chars.as_wtf8()),
195 Some('%') => (Some(Self::Percentage), chars.as_wtf8()),
196 Some(c) => (Some(Self::Unknown(c)), chars.as_wtf8()),
197 _ => (None, text),
198 }
199 }
200}
201
202#[derive(Clone, Copy, Debug, PartialEq, Eq)]
203pub struct FormatSpec {
204 fill: Option<CodePoint>,
205 align: Option<FormatAlign>,
206 align_specified: bool,
207 sign: Option<FormatSign>,
208 no_neg_0: bool,
209 alternate_form: bool,
210 width: Option<usize>,
211 grouping_option: Option<FormatGrouping>,
212 precision: Option<usize>,
213 frac_grouping_option: Option<FormatGrouping>,
214 format_type: Option<FormatType>,
215}
216
217fn get_num_digits(text: &Wtf8) -> usize {
218 for (index, character) in text.code_point_indices() {
219 if !character.is_char_and(|c| c.is_ascii_digit()) {
220 return index;
221 }
222 }
223 text.len()
224}
225
226fn parse_fill_and_align(text: &Wtf8) -> (Option<CodePoint>, Option<FormatAlign>, &Wtf8) {
227 let char_indices: Vec<(usize, CodePoint)> = text.code_point_indices().take(3).collect();
228 if char_indices.is_empty() {
229 (None, None, text)
230 } else if char_indices.len() == 1 {
231 let (maybe_align, remaining) = FormatAlign::parse(text);
232 (None, maybe_align, remaining)
233 } else {
234 let (maybe_align, remaining) = FormatAlign::parse(&text[char_indices[1].0..]);
235 if maybe_align.is_some() {
236 (Some(char_indices[0].1), maybe_align, remaining)
237 } else {
238 let (only_align, only_align_remaining) = FormatAlign::parse(text);
239 (None, only_align, only_align_remaining)
240 }
241 }
242}
243
244fn parse_number(text: &Wtf8) -> Result<(Option<usize>, &Wtf8), FormatSpecError> {
245 let num_digits: usize = get_num_digits(text);
246 if num_digits == 0 {
247 return Ok((None, text));
248 }
249 if let Some(num) = parse_usize(&text[..num_digits]) {
250 Ok((Some(num), &text[num_digits..]))
251 } else {
252 Err(FormatSpecError::DecimalDigitsTooMany)
254 }
255}
256
257fn parse_alternate_form(text: &Wtf8) -> (bool, &Wtf8) {
258 let mut chars = text.code_points();
259 match chars.next().and_then(CodePoint::to_char) {
260 Some('#') => (true, chars.as_wtf8()),
261 _ => (false, text),
262 }
263}
264
265fn parse_no_negative_zero(text: &Wtf8) -> (bool, &Wtf8) {
266 let mut chars = text.code_points();
267 match chars.next().and_then(CodePoint::to_char) {
268 Some('z') => (true, chars.as_wtf8()),
269 _ => (false, text),
270 }
271}
272
273fn parse_zero(text: &Wtf8) -> (bool, &Wtf8) {
274 let mut chars = text.code_points();
275 match chars.next().and_then(CodePoint::to_char) {
276 Some('0') => (true, chars.as_wtf8()),
277 _ => (false, text),
278 }
279}
280
281fn parse_char(text: &Wtf8, expected: char) -> (bool, &Wtf8) {
282 let mut chars = text.code_points();
283 if chars.next().and_then(CodePoint::to_char) == Some(expected) {
284 (true, chars.as_wtf8())
285 } else {
286 (false, text)
287 }
288}
289
290fn parse_precision(
291 text: &Wtf8,
292) -> Result<(Option<usize>, Option<FormatGrouping>, &Wtf8), FormatSpecError> {
293 let (dot, text) = parse_char(text, '.');
294 if !dot {
295 return Ok((None, None, text));
296 }
297 let (precision, text) = parse_number(text)?;
298 if let Some(precision) = precision
299 && precision > i32::MAX as usize
300 {
301 return Err(FormatSpecError::PrecisionTooBig);
302 }
303 let mut frac_grouping = None;
304 let (comma, text) = parse_char(text, ',');
305 if comma {
306 frac_grouping = Some(FormatGrouping::Comma);
307 }
308 let (underscore, text) = parse_char(text, '_');
309 if underscore {
310 if frac_grouping.is_some() {
311 return Err(FormatSpecError::ExclusiveFormat(',', '_'));
312 }
313 frac_grouping = Some(FormatGrouping::Underscore);
314 }
315 let (trailing_comma, _) = parse_char(text, ',');
316 if trailing_comma && frac_grouping == Some(FormatGrouping::Underscore) {
317 return Err(FormatSpecError::ExclusiveFormat(',', '_'));
318 }
319 if precision.is_none() && frac_grouping.is_none() {
321 return Err(FormatSpecError::PrecisionMissing);
322 }
323 Ok((precision, frac_grouping, text))
324}
325
326impl FormatSpec {
327 pub fn parse(text: impl AsRef<Wtf8>) -> Result<Self, FormatSpecError> {
328 Self::_parse(text.as_ref())
329 }
330
331 fn _parse(text: &Wtf8) -> Result<Self, FormatSpecError> {
332 let (mut fill, mut align, text) = parse_fill_and_align(text);
333 let align_specified = align.is_some();
334 let (sign, text) = FormatSign::parse(text);
335 let (no_neg_0, text) = parse_no_negative_zero(text);
336 let (alternate_form, text) = parse_alternate_form(text);
337 let (zero, text) = parse_zero(text);
338 let (width, text) = parse_number(text)?;
339 if let Some(w) = width
340 && w > i32::MAX as usize
341 {
342 return Err(FormatSpecError::DecimalDigitsTooMany);
343 }
344 let (grouping_option, text) = FormatGrouping::parse(text);
345 if let Some(grouping) = grouping_option {
346 Self::validate_separator(grouping, text)?;
347 }
348 let (precision, frac_grouping_option, text) = parse_precision(text)?;
349 let (format_type, text) = FormatType::parse(text);
350 if !text.is_empty() {
351 return Err(FormatSpecError::InvalidFormatSpecifier);
352 }
353
354 if zero && fill.is_none() {
355 fill.replace('0'.into());
356 align = align.or(Some(FormatAlign::AfterSign));
357 }
358
359 Ok(Self {
360 fill,
361 align,
362 align_specified,
363 sign,
364 no_neg_0,
365 alternate_form,
366 width,
367 grouping_option,
368 precision,
369 frac_grouping_option,
370 format_type,
371 })
372 }
373
374 fn validate_separator(grouping: FormatGrouping, text: &Wtf8) -> Result<(), FormatSpecError> {
375 let mut chars = text.code_points().peekable();
376 let grouping_char = char::from(grouping);
377 match chars.peek().and_then(|cp| CodePoint::to_char(*cp)) {
378 Some(c) if c == ',' || c == '_' => {
379 if c == grouping_char {
380 Err(FormatSpecError::UnspecifiedFormat(c, c))
381 } else {
382 Err(FormatSpecError::ExclusiveFormat(',', '_'))
383 }
384 }
385 _ => Ok(()),
386 }
387 }
388
389 fn compute_fill_string(fill_char: CodePoint, fill_chars_needed: i32) -> Wtf8Buf {
390 (0..fill_chars_needed).map(|_| fill_char).collect()
391 }
392
393 fn add_magnitude_separators_for_char(
394 magnitude_str: String,
395 inter: i32,
396 sep: char,
397 disp_digit_cnt: i32,
398 ) -> String {
399 let int_len = if inter == 4 {
406 magnitude_str.len()
407 } else {
408 magnitude_str
409 .bytes()
410 .position(|b| !b.is_ascii_digit())
411 .unwrap_or(magnitude_str.len())
412 };
413 if int_len == 0 {
416 return magnitude_str;
417 }
418 let magnitude_int_str = magnitude_str[..int_len].to_string();
419 let remainder = &magnitude_str[int_len..];
420 let dec_digit_cnt = magnitude_str.len() as i32 - magnitude_int_str.len() as i32;
421 let int_digit_cnt = disp_digit_cnt - dec_digit_cnt;
422 let mut result = Self::separate_integer(magnitude_int_str, inter, sep, int_digit_cnt);
423 result.push_str(remainder);
424 result
425 }
426
427 fn separate_integer(
428 magnitude_str: String,
429 inter: i32,
430 sep: char,
431 disp_digit_cnt: i32,
432 ) -> String {
433 let magnitude_len = magnitude_str.len() as i32;
434 let offset = (disp_digit_cnt % (inter + 1) == 0) as i32;
435 let disp_digit_cnt = disp_digit_cnt + offset;
436 let pad_cnt = disp_digit_cnt - magnitude_len;
437 let sep_cnt = disp_digit_cnt / (inter + 1);
438 let diff = pad_cnt - sep_cnt;
439 if pad_cnt > 0 && diff > 0 {
440 let padding = "0".repeat(diff as usize);
442 let padded_num = format!("{padding}{magnitude_str}");
443 Self::insert_separator(padded_num, inter, sep, sep_cnt)
444 } else {
445 let sep_cnt = (magnitude_len - 1) / inter;
447 Self::insert_separator(magnitude_str, inter, sep, sep_cnt)
448 }
449 }
450
451 fn insert_separator(mut magnitude_str: String, inter: i32, sep: char, sep_cnt: i32) -> String {
452 let magnitude_len = magnitude_str.len() as i32;
453 for i in 1..=sep_cnt {
454 magnitude_str.insert((magnitude_len - inter * i) as usize, sep);
455 }
456 magnitude_str
457 }
458
459 fn validate_format(&self, default_format_type: FormatType) -> Result<(), FormatSpecError> {
460 let format_type = self.format_type.as_ref().unwrap_or(&default_format_type);
461 match (&self.grouping_option, format_type) {
462 (
463 Some(FormatGrouping::Comma),
464 FormatType::String
465 | FormatType::Character
466 | FormatType::Binary
467 | FormatType::Octal
468 | FormatType::Hex(_)
469 | FormatType::Number(_)
470 | FormatType::Unknown(_),
471 ) => {
472 let ch = char::from(format_type);
473 Err(FormatSpecError::UnspecifiedFormat(',', ch))
474 }
475 (
476 Some(FormatGrouping::Underscore),
477 FormatType::String
478 | FormatType::Character
479 | FormatType::Number(_)
480 | FormatType::Unknown(_),
481 ) => {
482 let ch = char::from(format_type);
483 Err(FormatSpecError::UnspecifiedFormat('_', ch))
484 }
485 _ => Ok(()),
486 }?;
487 if let Some(grouping) = self.frac_grouping_option
488 && matches!(format_type, FormatType::Number(_))
489 {
490 let ch = char::from(format_type);
491 return Err(FormatSpecError::UnspecifiedFormat(char::from(grouping), ch));
492 }
493 Ok(())
494 }
495
496 fn formatted_magnitude_is_zero(magnitude: &str) -> bool {
497 let mut saw_digit = false;
498 for byte in magnitude.bytes() {
499 if byte.is_ascii_digit() {
500 saw_digit = true;
501 if byte != b'0' {
502 return false;
503 }
504 }
505 }
506 saw_digit
507 }
508
509 fn is_negative_after_zero_coercion(&self, num: f64, magnitude: &str) -> bool {
510 num.is_sign_negative()
511 && !num.is_nan()
512 && !(self.no_neg_0 && Self::formatted_magnitude_is_zero(magnitude))
513 }
514
515 fn validate_complex_padding_and_alignment(&self) -> Result<(), FormatSpecError> {
516 match &self.fill.unwrap_or_else(|| ' '.into()).to_char() {
517 Some('0') => Err(FormatSpecError::ZeroPadding),
518 _ if self.align == Some(FormatAlign::AfterSign) => Err(FormatSpecError::AlignmentFlag),
519 _ => Ok(()),
520 }
521 }
522
523 const fn get_separator_interval(&self) -> usize {
524 match self.format_type {
525 Some(FormatType::Binary | FormatType::Octal | FormatType::Hex(_)) => 4,
526 Some(
527 FormatType::Decimal
528 | FormatType::FixedPoint(_)
529 | FormatType::GeneralFormat(_)
530 | FormatType::Exponent(_)
531 | FormatType::Percentage
532 | FormatType::Number(_),
533 ) => 3,
534 None => 3,
535 _ => panic!("Separators only valid for numbers!"),
536 }
537 }
538
539 fn add_magnitude_separators(&self, magnitude_str: String, prefix: &str) -> String {
540 match &self.grouping_option {
541 Some(fg) => {
542 let sep = char::from(fg);
543 let inter = self.get_separator_interval().try_into().unwrap();
544 let magnitude_len = magnitude_str.len();
545 let disp_digit_cnt = if self.fill == Some('0'.into())
546 && self.align == Some(FormatAlign::AfterSign)
547 {
548 let width = self.width.unwrap_or(magnitude_len) as i32
549 - prefix.len() as i32
550 - self.frac_separator_count(&magnitude_str) as i32;
551 cmp::max(width, magnitude_len as i32)
552 } else {
553 magnitude_len as i32
554 };
555 Self::add_magnitude_separators_for_char(magnitude_str, inter, sep, disp_digit_cnt)
556 }
557 None => magnitude_str,
558 }
559 }
560
561 fn frac_digit_span(&self, magnitude_str: &str) -> Option<(FormatGrouping, usize, usize)> {
562 let grouping = self.frac_grouping_option?;
563 let start = magnitude_str.find('.')? + 1;
564 let end = magnitude_str[start..]
565 .bytes()
566 .position(|b| !b.is_ascii_digit())
567 .map_or(magnitude_str.len(), |offset| start + offset);
568 (start < end).then_some((grouping, start, end))
569 }
570
571 fn frac_separator_count(&self, magnitude_str: &str) -> usize {
572 match self.frac_digit_span(magnitude_str) {
573 Some((_, start, end)) => (end - start - 1) / self.get_separator_interval(),
574 None => 0,
575 }
576 }
577
578 fn add_frac_separators(&self, magnitude_str: String) -> String {
579 let Some((grouping, start, end)) = self.frac_digit_span(&magnitude_str) else {
580 return magnitude_str;
581 };
582 let inter = self.get_separator_interval();
583 let sep = char::from(grouping);
584 let mut result = magnitude_str[..start].to_string();
585 let mut frac = &magnitude_str[start..end];
586 while frac.len() > inter {
587 result.push_str(&frac[..inter]);
588 result.push(sep);
589 frac = &frac[inter..];
590 }
591 result.push_str(frac);
592 result.push_str(&magnitude_str[end..]);
593 result
594 }
595
596 #[must_use]
598 pub fn has_locale_format(&self) -> bool {
599 matches!(self.format_type, Some(FormatType::Number(Case::Lower)))
600 }
601
602 #[must_use]
607 pub fn is_decimal_int_format(&self) -> bool {
608 matches!(
609 self.format_type,
610 None | Some(FormatType::Decimal | FormatType::Number(Case::Lower))
611 )
612 }
613
614 fn insert_locale_grouping(int_part: &str, locale: &LocaleInfo) -> String {
617 if locale.grouping.is_empty() || locale.thousands_sep.is_empty() || int_part.len() <= 1 {
618 return int_part.to_string();
619 }
620
621 let mut group_idx = 0;
622 let mut group_size = locale.grouping[0] as usize;
623
624 if group_size == 0 {
625 return int_part.to_string();
626 }
627
628 let len = int_part.len();
630 let mut groups: Vec<&str> = Vec::new();
631 let mut pos = len;
632
633 loop {
634 if pos <= group_size {
635 groups.push(&int_part[..pos]);
636 break;
637 }
638
639 groups.push(&int_part[pos - group_size..pos]);
640 pos -= group_size;
641
642 if group_idx + 1 < locale.grouping.len() {
644 let next = locale.grouping[group_idx + 1] as usize;
645 if next != 0 {
646 group_size = next;
647 group_idx += 1;
648 }
649 }
651 }
652
653 groups.reverse();
655 groups.join(&locale.thousands_sep)
656 }
657
658 fn apply_locale_formatting(magnitude_str: String, locale: &LocaleInfo) -> String {
660 let mut parts = magnitude_str.splitn(2, '.');
661 let int_part = parts.next().unwrap();
662 let grouped = Self::insert_locale_grouping(int_part, locale);
663
664 if let Some(frac_part) = parts.next() {
665 format!("{grouped}{}{frac_part}", locale.decimal_point)
666 } else {
667 grouped
668 }
669 }
670
671 pub fn format_int_locale(
673 &self,
674 num: &BigInt,
675 locale: &LocaleInfo,
676 ) -> Result<String, FormatSpecError> {
677 self.validate_format(FormatType::Decimal)?;
678 let magnitude = num.abs();
679
680 let raw_magnitude_str = match self.format_type {
681 Some(FormatType::Number(Case::Lower)) => self.format_int_radix(magnitude, 10),
682 _ => return self.format_int(num),
683 }?;
684 if self.no_neg_0 {
685 return Err(FormatSpecError::NegativeZeroCoercionNotAllowed("integer"));
686 }
687
688 let magnitude_str = Self::apply_locale_formatting(raw_magnitude_str, locale);
689
690 let format_sign = self.sign.unwrap_or(FormatSign::Minus);
691 let sign_str = match num.sign() {
692 Sign::Minus => "-",
693 _ => match format_sign {
694 FormatSign::Plus => "+",
695 FormatSign::Minus => "",
696 FormatSign::MinusOrSpace => " ",
697 },
698 };
699
700 Ok(
701 self.format_sign_and_align(
702 &AsciiStr::new(&magnitude_str),
703 sign_str,
704 FormatAlign::Right,
705 ),
706 )
707 }
708
709 pub fn format_float_locale(
711 &self,
712 num: f64,
713 locale: &LocaleInfo,
714 ) -> Result<String, FormatSpecError> {
715 self.validate_format(FormatType::FixedPoint(Case::Lower))?;
716 let precision = self.precision.unwrap_or(6);
717 let magnitude = num.abs();
718
719 let raw_magnitude_str = match &self.format_type {
720 Some(FormatType::Number(case)) => {
721 let precision = if precision == 0 { 1 } else { precision };
722 Ok(float::format_general(
723 precision,
724 magnitude,
725 *case,
726 self.alternate_form,
727 false,
728 ))
729 }
730 _ => return self.format_float(num),
731 }?;
732
733 let magnitude_str = Self::apply_locale_formatting(raw_magnitude_str, locale);
734
735 let format_sign = self.sign.unwrap_or(FormatSign::Minus);
736 let sign_str = if self.is_negative_after_zero_coercion(num, &magnitude_str) {
737 "-"
738 } else {
739 match format_sign {
740 FormatSign::Plus => "+",
741 FormatSign::Minus => "",
742 FormatSign::MinusOrSpace => " ",
743 }
744 };
745
746 Ok(
747 self.format_sign_and_align(
748 &AsciiStr::new(&magnitude_str),
749 sign_str,
750 FormatAlign::Right,
751 ),
752 )
753 }
754
755 pub fn format_complex_locale(
757 &self,
758 num: &Complex64,
759 locale: &LocaleInfo,
760 ) -> Result<String, FormatSpecError> {
761 self.validate_format(FormatType::FixedPoint(Case::Lower))?;
762 let locale_spec = Self {
766 format_type: Some(FormatType::GeneralFormat(Case::Lower)),
767 ..*self
768 };
769 let (formatted_re, formatted_im) = locale_spec.format_complex_re_im(num)?;
770
771 let grouped_re = if formatted_re.is_empty() {
773 formatted_re
774 } else {
775 let (sign, mag) = if formatted_re.starts_with('-')
777 || formatted_re.starts_with('+')
778 || formatted_re.starts_with(' ')
779 {
780 formatted_re.split_at(1)
781 } else {
782 ("", formatted_re.as_str())
783 };
784 format!(
785 "{sign}{}",
786 Self::apply_locale_formatting(mag.to_string(), locale)
787 )
788 };
789
790 let im_str = &formatted_im;
793 let (im_sign, im_rest) = if im_str.starts_with('+') || im_str.starts_with('-') {
794 im_str.split_at(1)
795 } else {
796 ("", im_str.as_str())
797 };
798 let im_mag = im_rest.strip_suffix('j').unwrap_or(im_rest);
799 let im_grouped = Self::apply_locale_formatting(im_mag.to_string(), locale);
800 let grouped_im = format!("{im_sign}{im_grouped}j");
801
802 let magnitude_str = format!("{grouped_re}{grouped_im}");
804
805 self.validate_complex_padding_and_alignment()?;
806 Ok(self.format_sign_and_align(&AsciiStr::new(&magnitude_str), "", FormatAlign::Right))
807 }
808
809 fn is_empty(&self) -> bool {
812 let Self {
813 fill,
814 align,
815 align_specified,
816 sign,
817 no_neg_0,
818 alternate_form,
819 width,
820 grouping_option,
821 precision,
822 frac_grouping_option,
823 format_type,
824 } = self;
825 fill.is_none()
826 && align.is_none()
827 && !align_specified
828 && sign.is_none()
829 && !no_neg_0
830 && !alternate_form
831 && width.is_none()
832 && grouping_option.is_none()
833 && precision.is_none()
834 && frac_grouping_option.is_none()
835 && format_type.is_none()
836 }
837
838 pub fn format_bool(&self, input: bool) -> Result<String, FormatSpecError> {
839 self.validate_format(FormatType::Decimal)?;
840 let x = u8::from(input);
841 match &self.format_type {
842 Some(
843 FormatType::Binary
844 | FormatType::Decimal
845 | FormatType::Octal
846 | FormatType::Number(Case::Lower)
847 | FormatType::Hex(_)
848 | FormatType::GeneralFormat(_)
849 | FormatType::Character,
850 ) => self.format_int(&BigInt::from_u8(x).unwrap()),
851 Some(FormatType::Exponent(_) | FormatType::FixedPoint(_) | FormatType::Percentage) => {
852 self.format_float(x as f64)
853 }
854 None if self.is_empty() => Ok(if input { "True" } else { "False" }.to_owned()),
858 None => self.format_int(&BigInt::from_u8(x).unwrap()),
859 Some(format_type) => {
860 let ch = char::from(format_type);
861 Err(FormatSpecError::UnknownFormatCode(ch, "bool"))
862 }
863 }
864 }
865
866 pub fn format_float(&self, num: f64) -> Result<String, FormatSpecError> {
867 self.validate_format(FormatType::FixedPoint(Case::Lower))?;
868 let precision = self.precision.unwrap_or(6);
869 let magnitude = num.abs();
870 let raw_magnitude_str: Result<String, FormatSpecError> = match &self.format_type {
871 Some(FormatType::FixedPoint(case)) => Ok(float::format_fixed(
872 precision,
873 magnitude,
874 *case,
875 self.alternate_form,
876 )),
877 Some(
878 FormatType::Decimal
879 | FormatType::Binary
880 | FormatType::Octal
881 | FormatType::Hex(_)
882 | FormatType::String
883 | FormatType::Character
884 | FormatType::Number(Case::Upper)
885 | FormatType::Unknown(_),
886 ) => {
887 let ch = char::from(self.format_type.as_ref().unwrap());
888 Err(FormatSpecError::UnknownFormatCode(ch, "float"))
889 }
890 Some(FormatType::GeneralFormat(case) | FormatType::Number(case)) => {
891 let precision = if precision == 0 { 1 } else { precision };
892 Ok(float::format_general(
893 precision,
894 magnitude,
895 *case,
896 self.alternate_form,
897 false,
898 ))
899 }
900 Some(FormatType::Exponent(case)) => Ok(float::format_exponent(
901 precision,
902 magnitude,
903 *case,
904 self.alternate_form,
905 )),
906 Some(FormatType::Percentage) => match magnitude {
907 magnitude if magnitude.is_nan() => Ok("nan%".to_owned()),
908 magnitude if magnitude.is_infinite() => Ok("inf%".to_owned()),
909 _ => {
910 let scaled = magnitude * 100.0;
911 if scaled.is_infinite() {
915 Ok("inf%".to_owned())
916 } else {
917 let capped = float::clamp_fmt_precision(precision);
918 let mut result = format!("{scaled:.capped$}");
919 let missing = precision.saturating_sub(capped);
922 if missing > 0 {
923 result.extend(core::iter::repeat_n('0', missing));
924 }
925 let point = float::decimal_point_or_empty(precision, self.alternate_form);
926 Ok(format!("{result}{point}%"))
927 }
928 }
929 },
930 None => match magnitude {
931 magnitude if magnitude.is_nan() => Ok("nan".to_owned()),
932 magnitude if magnitude.is_infinite() => Ok("inf".to_owned()),
933 _ => match self.precision {
934 Some(precision) => {
935 let precision = if precision == 0 { 1 } else { precision };
940 let s = float::format_general(
941 precision,
942 magnitude,
943 Case::Lower,
944 self.alternate_form,
945 true,
946 );
947 Ok(if s.bytes().any(|b| matches!(b, b'.' | b'e' | b'E')) {
948 s
949 } else {
950 format!("{s}.0")
951 })
952 }
953 None => {
954 let s = float::to_string(magnitude);
955 Ok(if self.alternate_form && !s.contains('.') {
960 match s.find(['e', 'E']) {
961 Some(pos) => format!("{}.{}", &s[..pos], &s[pos..]),
962 None => format!("{s}."),
963 }
964 } else {
965 s
966 })
967 }
968 },
969 },
970 };
971 let raw_magnitude_str = raw_magnitude_str?;
972 let format_sign = self.sign.unwrap_or(FormatSign::Minus);
973 let sign_str = if self.is_negative_after_zero_coercion(num, &raw_magnitude_str) {
974 "-"
975 } else {
976 match format_sign {
977 FormatSign::Plus => "+",
978 FormatSign::Minus => "",
979 FormatSign::MinusOrSpace => " ",
980 }
981 };
982 let magnitude_str = self.add_magnitude_separators(raw_magnitude_str, sign_str);
983 let magnitude_str = self.add_frac_separators(magnitude_str);
984 Ok(
985 self.format_sign_and_align(
986 &AsciiStr::new(&magnitude_str),
987 sign_str,
988 FormatAlign::Right,
989 ),
990 )
991 }
992
993 #[inline]
994 fn format_int_radix(&self, magnitude: BigInt, radix: u32) -> Result<String, FormatSpecError> {
995 match self.precision {
996 Some(_) => Err(FormatSpecError::PrecisionNotAllowed),
997 None => Ok(magnitude.to_str_radix(radix)),
998 }
999 }
1000
1001 pub fn format_int(&self, num: &BigInt) -> Result<String, FormatSpecError> {
1002 self.validate_format(FormatType::Decimal)?;
1003 let magnitude = num.abs();
1004 let prefix = if self.alternate_form {
1005 match self.format_type {
1006 Some(FormatType::Binary) => "0b",
1007 Some(FormatType::Octal) => "0o",
1008 Some(FormatType::Hex(Case::Lower)) => "0x",
1009 Some(FormatType::Hex(Case::Upper)) => "0X",
1010 _ => "",
1011 }
1012 } else {
1013 ""
1014 };
1015 let raw_magnitude_str = match self.format_type {
1016 Some(FormatType::Binary) => self.format_int_radix(magnitude, 2),
1017 Some(FormatType::Decimal) => self.format_int_radix(magnitude, 10),
1018 Some(FormatType::Octal) => self.format_int_radix(magnitude, 8),
1019 Some(FormatType::Hex(Case::Lower)) => self.format_int_radix(magnitude, 16),
1020 Some(FormatType::Hex(Case::Upper)) => match self.precision {
1021 Some(_) => Err(FormatSpecError::PrecisionNotAllowed),
1022 None => {
1023 let mut result = magnitude.to_str_radix(16);
1024 result.make_ascii_uppercase();
1025 Ok(result)
1026 }
1027 },
1028 Some(FormatType::Number(Case::Lower)) => self.format_int_radix(magnitude, 10),
1029 Some(FormatType::Number(Case::Upper)) => {
1030 Err(FormatSpecError::UnknownFormatCode('N', "int"))
1031 }
1032 Some(FormatType::String) => Err(FormatSpecError::UnknownFormatCode('s', "int")),
1033 Some(FormatType::Character) => {
1034 if self.precision.is_some() {
1035 Err(FormatSpecError::PrecisionNotAllowed)
1036 } else if self.no_neg_0 {
1037 Err(FormatSpecError::NegativeZeroCoercionNotAllowed("integer"))
1038 } else {
1039 match (self.sign, self.alternate_form) {
1040 (Some(_), _) => Err(FormatSpecError::NotAllowed("Sign")),
1041 (_, true) => Err(FormatSpecError::NotAllowed("Alternate form (#)")),
1042 _ => match num
1043 .to_i64()
1044 .filter(|code| core::ffi::c_long::try_from(*code).is_ok())
1045 {
1046 None => Err(FormatSpecError::IntTooLargeForCLong),
1047 Some(n @ 0..=0x10ffff) => {
1048 let ch = core::char::from_u32(n as u32).unwrap().to_string();
1049 return Ok(self.format_sign_and_align(&ch, "", FormatAlign::Right));
1050 }
1051 Some(_) => Err(FormatSpecError::CodeNotInRange),
1052 },
1053 }
1054 }
1055 }
1056 Some(
1057 FormatType::GeneralFormat(_)
1058 | FormatType::FixedPoint(_)
1059 | FormatType::Exponent(_)
1060 | FormatType::Percentage,
1061 ) => match num.to_f64() {
1062 Some(float) => return self.format_float(float),
1063 _ => Err(FormatSpecError::UnableToConvert),
1064 },
1065 Some(FormatType::Unknown(c)) => Err(FormatSpecError::UnknownFormatCode(c, "int")),
1066 None => self.format_int_radix(magnitude, 10),
1067 }?;
1068 if self.no_neg_0 {
1069 return Err(FormatSpecError::NegativeZeroCoercionNotAllowed("integer"));
1070 }
1071 let format_sign = self.sign.unwrap_or(FormatSign::Minus);
1072 let sign_str = match num.sign() {
1073 Sign::Minus => "-",
1074 _ => match format_sign {
1075 FormatSign::Plus => "+",
1076 FormatSign::Minus => "",
1077 FormatSign::MinusOrSpace => " ",
1078 },
1079 };
1080 let sign_prefix = format!("{sign_str}{prefix}");
1081 let magnitude_str = self.add_magnitude_separators(raw_magnitude_str, &sign_prefix);
1082 Ok(self.format_sign_and_align(
1083 &AsciiStr::new(&magnitude_str),
1084 &sign_prefix,
1085 FormatAlign::Right,
1086 ))
1087 }
1088
1089 pub fn format_string<T>(&self, s: &T) -> Result<String, FormatSpecError>
1090 where
1091 T: CharLen + Deref<Target = str>,
1092 {
1093 self.validate_format(FormatType::String)?;
1094 match self.format_type {
1095 Some(FormatType::String) | None => {
1096 if let Some(sign) = self.sign {
1098 return Err(FormatSpecError::StringSpecNotAllowed(match sign {
1099 FormatSign::MinusOrSpace => "Space",
1100 FormatSign::Plus | FormatSign::Minus => "Sign",
1101 }));
1102 }
1103 if self.no_neg_0 {
1104 return Err(FormatSpecError::NegativeZeroCoercionNotAllowed("string"));
1105 }
1106 if self.alternate_form {
1107 return Err(FormatSpecError::StringSpecNotAllowed("Alternate form (#)"));
1108 }
1109 if self.align == Some(FormatAlign::AfterSign) && self.align_specified {
1110 return Err(FormatSpecError::StringAlignmentFlag);
1111 }
1112 let truncated: String = match self.precision {
1115 Some(p) => s.deref().chars().take(p).collect(),
1116 None => s.deref().to_owned(),
1117 };
1118 let spec = Self {
1119 align: if self.align == Some(FormatAlign::AfterSign) {
1120 Some(FormatAlign::Left)
1121 } else {
1122 self.align
1123 },
1124 ..*self
1125 };
1126 Ok(spec.format_sign_and_align(&truncated, "", FormatAlign::Left))
1127 }
1128 _ => {
1129 let ch = char::from(self.format_type.as_ref().unwrap());
1130 Err(FormatSpecError::UnknownFormatCode(ch, "str"))
1131 }
1132 }
1133 }
1134
1135 pub fn format_complex(&self, num: &Complex64) -> Result<String, FormatSpecError> {
1136 let (formatted_re, formatted_im) = self.format_complex_re_im(num)?;
1137 let magnitude_str = if self.format_type.is_none() && !formatted_re.is_empty() {
1139 format!("({formatted_re}{formatted_im})")
1140 } else {
1141 format!("{formatted_re}{formatted_im}")
1142 };
1143 self.validate_complex_padding_and_alignment()?;
1144 Ok(self.format_sign_and_align(&AsciiStr::new(&magnitude_str), "", FormatAlign::Right))
1145 }
1146
1147 fn format_complex_re_im(&self, num: &Complex64) -> Result<(String, String), FormatSpecError> {
1148 let formatted_re =
1150 if num.re != 0.0 || num.re.is_negative_zero() || self.format_type.is_some() {
1151 let re = self.format_complex_float(num.re)?;
1152 let sign_re = if self.is_negative_after_zero_coercion(num.re, &re) {
1153 "-"
1154 } else {
1155 match self.sign.unwrap_or(FormatSign::Minus) {
1156 FormatSign::Plus => "+",
1157 FormatSign::Minus => "",
1158 FormatSign::MinusOrSpace => " ",
1159 }
1160 };
1161 format!("{sign_re}{re}")
1162 } else {
1163 String::new()
1164 };
1165
1166 let im = self.format_complex_float(num.im)?;
1168 let sign_im = if self.is_negative_after_zero_coercion(num.im, &im) {
1169 "-"
1170 } else if formatted_re.is_empty() {
1171 match self.sign.unwrap_or(FormatSign::Minus) {
1172 FormatSign::Plus => "+",
1173 FormatSign::Minus => "",
1174 FormatSign::MinusOrSpace => " ",
1175 }
1176 } else {
1177 "+"
1178 };
1179 Ok((formatted_re, format!("{sign_im}{im}j")))
1180 }
1181
1182 fn format_complex_float(&self, num: f64) -> Result<String, FormatSpecError> {
1183 self.validate_format(FormatType::FixedPoint(Case::Lower))?;
1184 let precision = self.precision.unwrap_or(6);
1185 let magnitude = num.abs();
1186 let general = |case| {
1189 float::format_general(
1190 if precision == 0 { 1 } else { precision },
1191 magnitude,
1192 case,
1193 self.alternate_form,
1194 false,
1195 )
1196 };
1197 let magnitude_str = match &self.format_type {
1198 Some(
1199 FormatType::Decimal
1200 | FormatType::Binary
1201 | FormatType::Octal
1202 | FormatType::Hex(_)
1203 | FormatType::String
1204 | FormatType::Character
1205 | FormatType::Number(Case::Upper)
1206 | FormatType::Percentage
1207 | FormatType::Unknown(_),
1208 ) => {
1209 let ch = char::from(self.format_type.as_ref().unwrap());
1210 Err(FormatSpecError::UnknownFormatCode(ch, "complex"))
1211 }
1212 Some(FormatType::FixedPoint(case)) => Ok(float::format_fixed(
1213 precision,
1214 magnitude,
1215 *case,
1216 self.alternate_form,
1217 )),
1218 Some(FormatType::GeneralFormat(case) | FormatType::Number(case)) => Ok(general(*case)),
1219 Some(FormatType::Exponent(case)) => Ok(float::format_exponent(
1220 precision,
1221 magnitude,
1222 *case,
1223 self.alternate_form,
1224 )),
1225 None => match magnitude {
1226 magnitude if magnitude.is_nan() => Ok("nan".to_owned()),
1227 magnitude if magnitude.is_infinite() => Ok("inf".to_owned()),
1228 _ if self.precision.is_some() => Ok(general(Case::Lower)),
1229 magnitude if magnitude.fract() == 0.0 => Ok(magnitude.trunc().to_string()),
1230 magnitude => Ok(magnitude.to_string()),
1231 },
1232 }?;
1233 let magnitude_str = match &self.grouping_option {
1234 Some(fg) => {
1235 let sep = char::from(fg);
1236 let inter = self.get_separator_interval().try_into().unwrap();
1237 let len = magnitude_str.len() as i32;
1238 Self::add_magnitude_separators_for_char(magnitude_str, inter, sep, len)
1239 }
1240 None => magnitude_str,
1241 };
1242 Ok(self.add_frac_separators(magnitude_str))
1243 }
1244
1245 fn format_sign_and_align<T>(
1246 &self,
1247 magnitude_str: &T,
1248 sign_str: &str,
1249 default_align: FormatAlign,
1250 ) -> String
1251 where
1252 T: CharLen + Deref<Target = str>,
1253 {
1254 let align = self.align.unwrap_or(default_align);
1255
1256 let num_chars = magnitude_str.char_len();
1257 let fill_char = self.fill.unwrap_or_else(|| ' '.into());
1258 let fill_chars_needed: i32 = self.width.map_or(0, |w| {
1259 cmp::max(0, (w as i32) - (num_chars as i32) - (sign_str.len() as i32))
1260 });
1261
1262 let magnitude_str = &**magnitude_str;
1263 match align {
1264 FormatAlign::Left => format!(
1265 "{}{}{}",
1266 sign_str,
1267 magnitude_str,
1268 Self::compute_fill_string(fill_char, fill_chars_needed)
1269 ),
1270 FormatAlign::Right => format!(
1271 "{}{}{}",
1272 Self::compute_fill_string(fill_char, fill_chars_needed),
1273 sign_str,
1274 magnitude_str
1275 ),
1276 FormatAlign::AfterSign => format!(
1277 "{}{}{}",
1278 sign_str,
1279 Self::compute_fill_string(fill_char, fill_chars_needed),
1280 magnitude_str
1281 ),
1282 FormatAlign::Center => {
1283 let left_fill_chars_needed = fill_chars_needed / 2;
1284 let right_fill_chars_needed = fill_chars_needed - left_fill_chars_needed;
1285 let left_fill_string = Self::compute_fill_string(fill_char, left_fill_chars_needed);
1286 let right_fill_string =
1287 Self::compute_fill_string(fill_char, right_fill_chars_needed);
1288 format!("{left_fill_string}{sign_str}{magnitude_str}{right_fill_string}")
1289 }
1290 }
1291 }
1292}
1293
1294pub trait CharLen {
1295 fn char_len(&self) -> usize;
1297}
1298
1299struct AsciiStr<'a> {
1300 inner: &'a str,
1301}
1302
1303impl<'a> AsciiStr<'a> {
1304 const fn new(inner: &'a str) -> Self {
1305 Self { inner }
1306 }
1307}
1308
1309impl CharLen for AsciiStr<'_> {
1310 fn char_len(&self) -> usize {
1311 self.inner.len()
1312 }
1313}
1314
1315impl CharLen for String {
1316 fn char_len(&self) -> usize {
1317 self.chars().count()
1318 }
1319}
1320
1321impl Deref for AsciiStr<'_> {
1322 type Target = str;
1323
1324 fn deref(&self) -> &Self::Target {
1325 self.inner
1326 }
1327}
1328
1329#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1330pub enum FormatSpecError {
1331 DecimalDigitsTooMany,
1332 PrecisionTooBig,
1333 PrecisionMissing,
1334 InvalidFormatSpecifier,
1335 UnspecifiedFormat(char, char),
1336 ExclusiveFormat(char, char),
1337 UnknownFormatCode(char, &'static str),
1338 PrecisionNotAllowed,
1339 NotAllowed(&'static str),
1340 UnableToConvert,
1341 CodeNotInRange,
1342 IntTooLargeForCLong,
1343 ZeroPadding,
1344 AlignmentFlag,
1345 NegativeZeroCoercionNotAllowed(&'static str),
1346 StringAlignmentFlag,
1347 StringSpecNotAllowed(&'static str),
1348 NotImplemented(char, &'static str),
1349}
1350
1351#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1352pub enum FormatParseError {
1353 UnmatchedBracket,
1354 MissingStartBracket,
1355 UnescapedStartBracketInLiteral,
1356 InvalidFormatSpecifier,
1357 UnknownConversion,
1358 EmptyAttribute,
1359 MissingRightBracket,
1360 InvalidCharacterAfterRightBracket,
1361 TooManyDecimalDigits,
1362}
1363
1364impl FromStr for FormatSpec {
1365 type Err = FormatSpecError;
1366 fn from_str(s: &str) -> Result<Self, Self::Err> {
1367 Self::parse(s)
1368 }
1369}
1370
1371#[derive(Debug, PartialEq, Eq)]
1372pub enum FieldNamePart {
1373 Attribute(Wtf8Buf),
1374 Index(usize),
1375 StringIndex(Wtf8Buf),
1376}
1377
1378impl FieldNamePart {
1379 fn parse_part(
1380 chars: &mut impl PeekingNext<Item = CodePoint>,
1381 ) -> Result<Option<Self>, FormatParseError> {
1382 chars
1383 .next()
1384 .map(|ch| match ch.to_char_lossy() {
1385 '.' => {
1386 let mut attribute = Wtf8Buf::new();
1387 for ch in chars.peeking_take_while(|ch| *ch != '.' && *ch != '[') {
1388 attribute.push(ch);
1389 }
1390 if attribute.is_empty() {
1391 Err(FormatParseError::EmptyAttribute)
1392 } else {
1393 Ok(Self::Attribute(attribute))
1394 }
1395 }
1396 '[' => {
1397 let mut index = Wtf8Buf::new();
1398 for ch in chars {
1399 if ch == ']' {
1400 return if index.is_empty() {
1401 Err(FormatParseError::EmptyAttribute)
1402 } else if let Some(index) = parse_usize(&index) {
1403 Ok(Self::Index(index))
1404 } else {
1405 Ok(Self::StringIndex(index))
1406 };
1407 }
1408 index.push(ch);
1409 }
1410 Err(FormatParseError::MissingRightBracket)
1411 }
1412 _ => Err(FormatParseError::InvalidCharacterAfterRightBracket),
1413 })
1414 .transpose()
1415 }
1416}
1417
1418#[derive(Debug, PartialEq, Eq)]
1419pub enum FieldType {
1420 Auto,
1421 Index(usize),
1422 Keyword(Wtf8Buf),
1423}
1424
1425#[derive(Debug, PartialEq, Eq)]
1426pub struct FieldName {
1427 pub field_type: FieldType,
1428 pub parts: Vec<FieldNamePart>,
1429}
1430
1431fn parse_usize(s: &Wtf8) -> Option<usize> {
1432 s.as_str().ok().and_then(|s| s.parse().ok())
1433}
1434
1435impl FieldName {
1436 pub fn parse(text: &Wtf8) -> Result<Self, FormatParseError> {
1437 let mut chars = text.code_points().peekable();
1438 let first: Wtf8Buf = chars
1439 .peeking_take_while(|ch| *ch != '.' && *ch != '[')
1440 .collect();
1441
1442 let field_type = if first.is_empty() {
1443 FieldType::Auto
1444 } else if let Some(index) = parse_usize(&first) {
1445 if index > isize::MAX as usize {
1448 return Err(FormatParseError::TooManyDecimalDigits);
1449 }
1450 FieldType::Index(index)
1451 } else if first
1452 .as_str()
1453 .is_ok_and(|s| s.bytes().all(|b| b.is_ascii_digit()))
1454 {
1455 return Err(FormatParseError::TooManyDecimalDigits);
1457 } else {
1458 FieldType::Keyword(first)
1459 };
1460
1461 let mut parts = Vec::new();
1462 while let Some(part) = FieldNamePart::parse_part(&mut chars)? {
1463 parts.push(part)
1464 }
1465
1466 Ok(Self { field_type, parts })
1467 }
1468}
1469
1470#[derive(Debug, PartialEq, Eq)]
1471pub enum FormatPart {
1472 Field {
1473 field_name: Wtf8Buf,
1474 conversion_spec: Option<CodePoint>,
1475 format_spec: Wtf8Buf,
1476 },
1477 Literal(Wtf8Buf),
1478}
1479
1480#[derive(Debug, PartialEq, Eq)]
1481pub struct FormatString {
1482 pub format_parts: Vec<FormatPart>,
1483}
1484
1485impl FormatString {
1486 fn parse_literal_single(text: &Wtf8) -> Result<(CodePoint, &Wtf8), FormatParseError> {
1487 let mut chars = text.code_points();
1488 let first_char = chars.next().unwrap();
1490 if first_char == '{' || first_char == '}' {
1492 let maybe_next_char = chars.next();
1493 return if maybe_next_char.is_none() || maybe_next_char.unwrap() != first_char {
1495 Err(FormatParseError::UnescapedStartBracketInLiteral)
1496 } else {
1497 Ok((first_char, chars.as_wtf8()))
1498 };
1499 }
1500 Ok((first_char, chars.as_wtf8()))
1501 }
1502
1503 fn parse_literal(text: &Wtf8) -> Result<(FormatPart, &Wtf8), FormatParseError> {
1504 let mut cur_text = text;
1505 let mut result_string = Wtf8Buf::new();
1506 while !cur_text.is_empty() {
1507 match Self::parse_literal_single(cur_text) {
1508 Ok((next_char, remaining)) => {
1509 result_string.push(next_char);
1510 cur_text = remaining;
1511 }
1512 Err(err) => {
1513 return if !result_string.is_empty() {
1514 Ok((FormatPart::Literal(result_string), cur_text))
1515 } else {
1516 Err(err)
1517 };
1518 }
1519 }
1520 }
1521 Ok((FormatPart::Literal(result_string), "".as_ref()))
1522 }
1523
1524 fn parse_part_in_brackets(text: &Wtf8) -> Result<FormatPart, FormatParseError> {
1525 let mut chars = text.code_points().peekable();
1526
1527 let mut left = Wtf8Buf::new();
1528 let mut right = Wtf8Buf::new();
1529
1530 let mut split = false;
1531 let mut selected = &mut left;
1532 let mut inside_brackets = false;
1533
1534 while let Some(char) = chars.next() {
1535 if char == '[' {
1536 inside_brackets = true;
1537
1538 selected.push(char);
1539
1540 while let Some(next_char) = chars.next() {
1541 selected.push(next_char);
1542
1543 if next_char == ']' {
1544 inside_brackets = false;
1545 break;
1546 }
1547 if chars.peek().is_none() {
1548 return Err(FormatParseError::MissingRightBracket);
1549 }
1550 }
1551 } else if char == ':' && !split && !inside_brackets {
1552 split = true;
1553 selected = &mut right;
1554 } else {
1555 selected.push(char);
1556 }
1557 }
1558
1559 let arg_part: &Wtf8 = &left;
1561
1562 let format_spec = if split { right } else { Wtf8Buf::new() };
1563
1564 let parts: Vec<&Wtf8> = arg_part.splitn(2, "!".as_ref()).collect();
1566 let arg_part = parts[0];
1568
1569 let conversion_spec = parts
1570 .get(1)
1571 .map(|conversion| {
1572 conversion
1574 .code_points()
1575 .exactly_one()
1576 .map_err(|_| FormatParseError::UnknownConversion)
1577 })
1578 .transpose()?;
1579
1580 Ok(FormatPart::Field {
1581 field_name: arg_part.to_owned(),
1582 conversion_spec,
1583 format_spec,
1584 })
1585 }
1586
1587 fn parse_spec(text: &Wtf8) -> Result<(FormatPart, &Wtf8), FormatParseError> {
1588 let mut nested = false;
1589 let mut end_bracket_pos = None;
1590 let mut left = Wtf8Buf::new();
1591
1592 for (idx, c) in text.code_point_indices() {
1594 if idx == 0 {
1595 if c != '{' {
1596 return Err(FormatParseError::MissingStartBracket);
1597 }
1598 } else if c == '{' {
1599 if nested {
1600 return Err(FormatParseError::InvalidFormatSpecifier);
1601 }
1602 nested = true;
1603 left.push(c);
1604 continue;
1605 } else if c == '}' {
1606 if nested {
1607 nested = false;
1608 left.push(c);
1609 continue;
1610 }
1611 end_bracket_pos = Some(idx);
1612 break;
1613 } else {
1614 left.push(c);
1615 }
1616 }
1617 if let Some(pos) = end_bracket_pos {
1618 let right = &text[pos..];
1619 let format_part = Self::parse_part_in_brackets(&left)?;
1620 Ok((format_part, &right[1..]))
1621 } else if text.len() == 1 {
1622 Err(FormatParseError::UnescapedStartBracketInLiteral)
1627 } else {
1628 Err(FormatParseError::UnmatchedBracket)
1629 }
1630 }
1631}
1632
1633pub trait FromTemplate<'a>: Sized {
1634 type Err;
1635 fn from_str(s: &'a Wtf8) -> Result<Self, Self::Err>;
1636}
1637
1638impl<'a> FromTemplate<'a> for FormatString {
1639 type Err = FormatParseError;
1640
1641 fn from_str(text: &'a Wtf8) -> Result<Self, Self::Err> {
1642 let mut cur_text: &Wtf8 = text;
1643 let mut parts: Vec<FormatPart> = Vec::new();
1644 while !cur_text.is_empty() {
1645 cur_text = Self::parse_literal(cur_text)
1648 .or_else(|_| Self::parse_spec(cur_text))
1649 .map(|(part, new_text)| {
1650 parts.push(part);
1651 new_text
1652 })?;
1653 }
1654 Ok(Self {
1655 format_parts: parts,
1656 })
1657 }
1658}
1659
1660#[cfg(test)]
1661mod tests {
1662 use super::*;
1663
1664 #[test]
1665 fn fill_and_align() {
1666 let parse_fill_and_align = |text| {
1667 let (fill, align, rest) = parse_fill_and_align(str::as_ref(text));
1668 (
1669 fill.and_then(CodePoint::to_char),
1670 align,
1671 rest.as_str().unwrap(),
1672 )
1673 };
1674 assert_eq!(
1675 parse_fill_and_align(" <"),
1676 (Some(' '), Some(FormatAlign::Left), "")
1677 );
1678 assert_eq!(
1679 parse_fill_and_align(" <22"),
1680 (Some(' '), Some(FormatAlign::Left), "22")
1681 );
1682 assert_eq!(
1683 parse_fill_and_align("<22"),
1684 (None, Some(FormatAlign::Left), "22")
1685 );
1686 assert_eq!(
1687 parse_fill_and_align(" ^^"),
1688 (Some(' '), Some(FormatAlign::Center), "^")
1689 );
1690 assert_eq!(
1691 parse_fill_and_align("==="),
1692 (Some('='), Some(FormatAlign::AfterSign), "=")
1693 );
1694 }
1695
1696 #[test]
1697 fn width_only() {
1698 let expected = Ok(FormatSpec {
1699 fill: None,
1700 align: None,
1701 align_specified: false,
1702 sign: None,
1703 no_neg_0: false,
1704 alternate_form: false,
1705 width: Some(33),
1706 grouping_option: None,
1707 precision: None,
1708 frac_grouping_option: None,
1709 format_type: None,
1710 });
1711 assert_eq!(FormatSpec::parse("33"), expected);
1712 }
1713
1714 #[test]
1715 fn fill_and_width() {
1716 let expected = Ok(FormatSpec {
1717 fill: Some('<'.into()),
1718 align: Some(FormatAlign::Right),
1719 align_specified: true,
1720 sign: None,
1721 no_neg_0: false,
1722 alternate_form: false,
1723 width: Some(33),
1724 grouping_option: None,
1725 precision: None,
1726 frac_grouping_option: None,
1727 format_type: None,
1728 });
1729 assert_eq!(FormatSpec::parse("<>33"), expected);
1730 }
1731
1732 #[test]
1733 fn all() {
1734 let expected = Ok(FormatSpec {
1735 fill: Some('<'.into()),
1736 align: Some(FormatAlign::Right),
1737 align_specified: true,
1738 sign: Some(FormatSign::Minus),
1739 no_neg_0: false,
1740 alternate_form: true,
1741 width: Some(23),
1742 grouping_option: Some(FormatGrouping::Comma),
1743 precision: Some(11),
1744 frac_grouping_option: None,
1745 format_type: Some(FormatType::Binary),
1746 });
1747 assert_eq!(FormatSpec::parse("<>-#23,.11b"), expected);
1748 }
1749
1750 fn format_bool(text: &str, value: bool) -> Result<String, FormatSpecError> {
1751 FormatSpec::parse(text).and_then(|spec| spec.format_bool(value))
1752 }
1753
1754 #[test]
1755 fn format_bool_basic() {
1756 assert_eq!(format_bool("b", true), Ok("1".to_owned()));
1757 assert_eq!(format_bool("b", false), Ok("0".to_owned()));
1758 assert_eq!(format_bool("d", true), Ok("1".to_owned()));
1759 assert_eq!(format_bool("d", false), Ok("0".to_owned()));
1760 assert_eq!(format_bool("o", true), Ok("1".to_owned()));
1761 assert_eq!(format_bool("o", false), Ok("0".to_owned()));
1762 assert_eq!(format_bool("n", true), Ok("1".to_owned()));
1763 assert_eq!(format_bool("n", false), Ok("0".to_owned()));
1764 assert_eq!(format_bool("x", true), Ok("1".to_owned()));
1765 assert_eq!(format_bool("x", false), Ok("0".to_owned()));
1766 assert_eq!(format_bool("X", true), Ok("1".to_owned()));
1767 assert_eq!(format_bool("X", false), Ok("0".to_owned()));
1768 assert_eq!(format_bool("g", true), Ok("1".to_owned()));
1769 assert_eq!(format_bool("g", false), Ok("0".to_owned()));
1770 assert_eq!(format_bool("G", true), Ok("1".to_owned()));
1771 assert_eq!(format_bool("G", false), Ok("0".to_owned()));
1772 assert_eq!(format_bool("c", true), Ok("\x01".to_owned()));
1773 assert_eq!(format_bool("c", false), Ok("\x00".to_owned()));
1774 assert_eq!(format_bool("e", true), Ok("1.000000e+00".to_owned()));
1775 assert_eq!(format_bool("e", false), Ok("0.000000e+00".to_owned()));
1776 assert_eq!(format_bool("E", true), Ok("1.000000E+00".to_owned()));
1777 assert_eq!(format_bool("E", false), Ok("0.000000E+00".to_owned()));
1778 assert_eq!(format_bool("f", true), Ok("1.000000".to_owned()));
1779 assert_eq!(format_bool("f", false), Ok("0.000000".to_owned()));
1780 assert_eq!(format_bool("F", true), Ok("1.000000".to_owned()));
1781 assert_eq!(format_bool("F", false), Ok("0.000000".to_owned()));
1782 assert_eq!(format_bool("%", true), Ok("100.000000%".to_owned()));
1783 assert_eq!(format_bool("%", false), Ok("0.000000%".to_owned()));
1784 }
1785
1786 #[test]
1787 fn format_bool_without_a_presentation_type() {
1788 assert_eq!(format_bool("", true), Ok("True".to_owned()));
1790 assert_eq!(format_bool("", false), Ok("False".to_owned()));
1791
1792 assert_eq!(format_bool("5", true), Ok(" 1".to_owned()));
1794 assert_eq!(format_bool("<5", true), Ok("1 ".to_owned()));
1795 assert_eq!(format_bool(">5", false), Ok(" 0".to_owned()));
1796 assert_eq!(format_bool("^5", true), Ok(" 1 ".to_owned()));
1797 assert_eq!(format_bool("05", true), Ok("00001".to_owned()));
1798 assert_eq!(format_bool("+", true), Ok("+1".to_owned()));
1799 assert_eq!(format_bool(" ", false), Ok(" 0".to_owned()));
1800 assert_eq!(format_bool(",", true), Ok("1".to_owned()));
1801 assert_eq!(format_bool("<", true), Ok("1".to_owned()));
1802
1803 assert_eq!(
1805 format_bool(".2", true),
1806 Err(FormatSpecError::PrecisionNotAllowed)
1807 );
1808 assert_eq!(
1809 format_bool("z", true),
1810 Err(FormatSpecError::NegativeZeroCoercionNotAllowed("integer"))
1811 );
1812 }
1813
1814 #[test]
1815 fn format_string_zero_padding_uses_left_alignment() {
1816 let spec = FormatSpec::parse("08s").unwrap();
1817 let value = "result".to_owned();
1818
1819 assert_eq!(spec.format_string(&value), Ok("result00".to_owned()));
1820 }
1821
1822 #[test]
1823 fn format_string_explicit_after_sign_alignment_is_invalid() {
1824 let spec = FormatSpec::parse("=8s").unwrap();
1825 let value = "result".to_owned();
1826
1827 assert_eq!(
1828 spec.format_string(&value),
1829 Err(FormatSpecError::StringAlignmentFlag)
1830 );
1831 }
1832
1833 #[test]
1834 fn format_string_rejects_sign_space_and_alternate_form() {
1835 let value = "result".to_owned();
1836 let cases = [
1837 ("+", "Sign"),
1838 ("-", "Sign"),
1839 ("+8s", "Sign"),
1840 (" ", "Space"),
1841 (" 8s", "Space"),
1842 ("#", "Alternate form (#)"),
1843 ("#8s", "Alternate form (#)"),
1844 ];
1845
1846 for (text, flag) in cases {
1847 let spec = FormatSpec::parse(text).unwrap();
1848 assert_eq!(
1849 spec.format_string(&value),
1850 Err(FormatSpecError::StringSpecNotAllowed(flag)),
1851 "{text}"
1852 );
1853 }
1854 }
1855
1856 #[test]
1857 fn format_string_reports_the_flag_cpython_reports_first() {
1858 let value = "result".to_owned();
1859 let cases = [
1862 ("+z#5", FormatSpecError::StringSpecNotAllowed("Sign")),
1863 (
1864 "x=z#5",
1865 FormatSpecError::NegativeZeroCoercionNotAllowed("string"),
1866 ),
1867 (
1868 "x=#5",
1869 FormatSpecError::StringSpecNotAllowed("Alternate form (#)"),
1870 ),
1871 ];
1872
1873 for (text, expected) in cases {
1874 let spec = FormatSpec::parse(text).unwrap();
1875 assert_eq!(spec.format_string(&value), Err(expected), "{text}");
1876 }
1877 }
1878
1879 #[test]
1880 fn format_complex_rejects_zero_padding_before_after_sign_alignment() {
1881 for text in [
1882 "08.1f", "=08.1f", "0=8.1f", "#08.1f", "0>8.1f", "0<8.1f", "0^8.1f",
1883 ] {
1884 let spec = FormatSpec::parse(text).unwrap();
1885 assert_eq!(
1886 spec.format_complex(&Complex64::new(1.0, 2.0)),
1887 Err(FormatSpecError::ZeroPadding),
1888 "{text}"
1889 );
1890 }
1891
1892 let spec = FormatSpec::parse("=8.1f").unwrap();
1893 assert_eq!(
1894 spec.format_complex(&Complex64::new(1.0, 2.0)),
1895 Err(FormatSpecError::AlignmentFlag)
1896 );
1897 }
1898
1899 #[test]
1900 fn format_int_zero_padding_stays_after_sign() {
1901 let spec = FormatSpec::parse("08").unwrap();
1902
1903 assert_eq!(
1904 spec.format_int(&BigInt::from(-42)),
1905 Ok("-0000042".to_owned())
1906 );
1907 }
1908
1909 #[test]
1910 fn format_complex_locale_rejects_zero_padding_before_after_sign_alignment() {
1911 let locale = LocaleInfo {
1912 thousands_sep: String::new(),
1913 decimal_point: ".".to_owned(),
1914 grouping: vec![],
1915 };
1916 for text in ["08n", "=08n", "0=8n", "#08n", "0>8n", "0<8n", "0^8n"] {
1917 let spec = FormatSpec::parse(text).unwrap();
1918 assert_eq!(
1919 spec.format_complex_locale(&Complex64::new(1.0, 2.0), &locale),
1920 Err(FormatSpecError::ZeroPadding),
1921 "{text}"
1922 );
1923 }
1924
1925 let spec = FormatSpec::parse("=8n").unwrap();
1926 assert_eq!(
1927 spec.format_complex_locale(&Complex64::new(1.0, 2.0), &locale),
1928 Err(FormatSpecError::AlignmentFlag)
1929 );
1930 }
1931
1932 #[test]
1933 fn format_negative_zero_coercion() {
1934 let int_spec = FormatSpec::parse("z8").unwrap();
1935 assert_eq!(
1936 int_spec.format_int(&BigInt::from(-42)),
1937 Err(FormatSpecError::NegativeZeroCoercionNotAllowed("integer"))
1938 );
1939 assert_eq!(
1940 FormatSpec::parse("zs")
1941 .unwrap()
1942 .format_int(&BigInt::from(0)),
1943 Err(FormatSpecError::UnknownFormatCode('s', "int"))
1944 );
1945 assert_eq!(
1946 FormatSpec::parse("z.1d")
1947 .unwrap()
1948 .format_int(&BigInt::from(0)),
1949 Err(FormatSpecError::PrecisionNotAllowed)
1950 );
1951 assert_eq!(
1952 FormatSpec::parse("+zc")
1953 .unwrap()
1954 .format_int(&BigInt::from(0)),
1955 Err(FormatSpecError::NegativeZeroCoercionNotAllowed("integer"))
1956 );
1957
1958 let float_spec = FormatSpec::parse("z.2f").unwrap();
1959 assert_eq!(float_spec.format_float(-0.0001), Ok("0.00".to_owned()));
1960
1961 let complex_spec = FormatSpec::parse("z").unwrap();
1962 assert_eq!(
1963 complex_spec.format_complex(&Complex64::new(-0.0, -0.0)),
1964 Ok("(0+0j)".to_owned())
1965 );
1966 let pure_imaginary = Complex64::new(0.0, -0.0);
1967 assert_eq!(
1968 FormatSpec::parse("+z")
1969 .unwrap()
1970 .format_complex(&pure_imaginary),
1971 Ok("+0j".to_owned())
1972 );
1973 assert_eq!(
1974 FormatSpec::parse(" z")
1975 .unwrap()
1976 .format_complex(&pure_imaginary),
1977 Ok(" 0j".to_owned())
1978 );
1979
1980 let string_value = "value".to_owned();
1981 assert_eq!(
1982 FormatSpec::parse("z").unwrap().format_string(&string_value),
1983 Err(FormatSpecError::NegativeZeroCoercionNotAllowed("string"))
1984 );
1985 assert_eq!(
1986 FormatSpec::parse("zd")
1987 .unwrap()
1988 .format_string(&string_value),
1989 Err(FormatSpecError::UnknownFormatCode('d', "str"))
1990 );
1991 assert_eq!(
1992 FormatSpec::parse("zs").unwrap().format_bool(false),
1993 Err(FormatSpecError::UnknownFormatCode('s', "bool"))
1994 );
1995
1996 let locale = LocaleInfo {
1997 thousands_sep: ",".to_owned(),
1998 decimal_point: ".".to_owned(),
1999 grouping: vec![3, 0],
2000 };
2001 let locale_spec = FormatSpec::parse("zn").unwrap();
2002 assert_eq!(
2003 locale_spec.format_float_locale(-0.0, &locale),
2004 Ok("0".to_owned())
2005 );
2006 assert_eq!(
2007 locale_spec.format_complex_locale(&Complex64::new(-0.0, -0.0), &locale),
2008 Ok("0+0j".to_owned())
2009 );
2010 assert_eq!(
2011 FormatSpec::parse("z.1n")
2012 .unwrap()
2013 .format_int_locale(&BigInt::from(0), &locale),
2014 Err(FormatSpecError::PrecisionNotAllowed)
2015 );
2016 }
2017
2018 #[test]
2019 fn format_int() {
2020 assert_eq!(
2021 FormatSpec::parse("d")
2022 .unwrap()
2023 .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
2024 Ok("16".to_owned())
2025 );
2026 assert_eq!(
2027 FormatSpec::parse("x")
2028 .unwrap()
2029 .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
2030 Ok("10".to_owned())
2031 );
2032 assert_eq!(
2033 FormatSpec::parse("b")
2034 .unwrap()
2035 .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
2036 Ok("10000".to_owned())
2037 );
2038 assert_eq!(
2039 FormatSpec::parse("o")
2040 .unwrap()
2041 .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
2042 Ok("20".to_owned())
2043 );
2044 assert_eq!(
2045 FormatSpec::parse("+d")
2046 .unwrap()
2047 .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
2048 Ok("+16".to_owned())
2049 );
2050 assert_eq!(
2051 FormatSpec::parse("^ 5d")
2052 .unwrap()
2053 .format_int(&BigInt::from_bytes_be(Sign::Minus, b"\x10")),
2054 Ok(" -16 ".to_owned())
2055 );
2056 assert_eq!(
2057 FormatSpec::parse("0>+#10x")
2058 .unwrap()
2059 .format_int(&BigInt::from_bytes_be(Sign::Plus, b"\x10")),
2060 Ok("00000+0x10".to_owned())
2061 );
2062 }
2063
2064 #[test]
2065 fn format_int_sep() {
2066 let spec = FormatSpec::parse(",").expect("");
2067 assert_eq!(spec.grouping_option, Some(FormatGrouping::Comma));
2068 assert_eq!(
2069 spec.format_int(&BigInt::from_str("1234567890123456789012345678").unwrap()),
2070 Ok("1,234,567,890,123,456,789,012,345,678".to_owned())
2071 );
2072 }
2073
2074 #[test]
2075 fn format_int_width_and_grouping() {
2076 let spec = FormatSpec::parse("10,").unwrap();
2078 let result = spec.format_int(&BigInt::from(1234)).unwrap();
2079 assert_eq!(result, " 1,234"); }
2081
2082 #[test]
2083 fn format_int_padding_with_grouping() {
2084 let spec1 = FormatSpec::parse("010,").unwrap();
2086 let result1 = spec1.format_int(&BigInt::from(1234)).unwrap();
2087 assert_eq!(result1, "00,001,234");
2088
2089 let spec2 = FormatSpec::parse("010,").unwrap();
2091 let result2 = spec2.format_int(&BigInt::from(-1234)).unwrap();
2092 assert_eq!(result2, "-0,001,234");
2093
2094 let spec3 = FormatSpec::parse("=10,").unwrap();
2096 let result3 = spec3.format_int(&BigInt::from(-1234)).unwrap();
2097 assert_eq!(result3, "- 1,234");
2098
2099 let spec4 = FormatSpec::parse("=10,").unwrap();
2101 let result4 = spec4.format_int(&BigInt::from(1234)).unwrap();
2102 assert_eq!(result4, " 1,234");
2103 }
2104
2105 #[test]
2106 fn format_int_non_aftersign_zero_padding() {
2107 let spec = FormatSpec::parse("0>10,").unwrap();
2109 let result = spec.format_int(&BigInt::from(1234)).unwrap();
2110 assert_eq!(result, "000001,234");
2111 }
2112
2113 fn fmt_float(spec: &str, value: f64) -> String {
2114 FormatSpec::parse(spec)
2115 .unwrap()
2116 .format_float(value)
2117 .unwrap()
2118 }
2119
2120 #[test]
2121 fn format_float_grouping_never_touches_exponent() {
2122 assert_eq!(fmt_float(",g", 1e20), "1e+20");
2125 assert_eq!(fmt_float("_g", 1e-10), "1e-10");
2126 assert_eq!(fmt_float(",e", 1e20), "1.000000e+20");
2127 assert_eq!(fmt_float(",", 1e16), "1e+16");
2128 assert_eq!(fmt_float(",.0%", 1.0), "100%");
2129 assert_eq!(fmt_float(",.2%", 12345.0), "1,234,500.00%");
2130 assert_eq!(fmt_float(",", 1234567.0), "1,234,567.0");
2132 }
2133
2134 #[test]
2135 fn format_float_grouping_inf_nan() {
2136 assert_eq!(fmt_float(",", f64::INFINITY), "inf");
2139 assert_eq!(fmt_float("06,", f64::INFINITY), "000inf");
2140 assert_eq!(fmt_float("06,", f64::NAN), "000nan");
2141 assert_eq!(fmt_float("06,%", f64::INFINITY), "00inf%");
2142 }
2143
2144 #[test]
2145 fn format_float_fractional_grouping() {
2146 assert_eq!(fmt_float(".6,f", 1234.56789), "1234.567,890");
2149 assert_eq!(fmt_float(".7,f", 1234.56789), "1234.567,890,0");
2150 assert_eq!(fmt_float(".4,f", 1.1), "1.100,0");
2151 assert_eq!(fmt_float(".3,f", 1.1), "1.100");
2152 assert_eq!(fmt_float(".6_f", 1234.56789), "1234.567_890");
2153 assert_eq!(fmt_float(".,f", 1.1), "1.100,000");
2155 assert_eq!(fmt_float(",.6,f", 1234.56789), "1,234.567,890");
2157 assert_eq!(fmt_float(",.6_f", 1234.56789), "1,234.567_890");
2158 assert_eq!(fmt_float("_.6,f", 1234.56789), "1_234.567,890");
2159 }
2160
2161 #[test]
2162 fn format_float_fractional_grouping_never_touches_tail() {
2163 assert_eq!(fmt_float(".6,e", 12345678900.0), "1.234,568e+10");
2166 assert_eq!(fmt_float(".6,E", 1234.5678), "1.234,568E+03");
2167 assert_eq!(fmt_float(".8,%", 1.2345e-05), "0.001,234,50%");
2168 assert_eq!(fmt_float(".6,f", f64::INFINITY), "inf");
2170 assert_eq!(fmt_float(".6,f", f64::NAN), "nan");
2171 assert_eq!(fmt_float(".0,f", 1234.56789), "1235");
2172 }
2173
2174 #[test]
2175 fn format_float_fractional_grouping_counts_toward_width() {
2176 assert_eq!(fmt_float("020.6,f", 1234.56789), "000000001234.567,890");
2178 assert_eq!(fmt_float("015.6,f", 1.5), "0000001.500,000");
2179 assert_eq!(fmt_float("<20.6,f", 1234.56789), "1234.567,890 ");
2180 assert_eq!(fmt_float("020,.6,f", 1234.56789), "0,000,001,234.567,890");
2182 assert_eq!(fmt_float("+020,.6_f", 1e-10), "+000,000,000.000_000");
2183 assert_eq!(fmt_float("= 015,.6,E", 1234.0), " 01.234,000E+03");
2184 assert_eq!(fmt_float("-015_._e", 1.1), "001.100_000e+00");
2185 }
2186
2187 #[test]
2188 fn format_parse_fractional_grouping_errors() {
2189 assert_eq!(
2191 FormatSpec::parse(".,_f"),
2192 Err(FormatSpecError::ExclusiveFormat(',', '_'))
2193 );
2194 assert_eq!(
2195 FormatSpec::parse("._,f"),
2196 Err(FormatSpecError::ExclusiveFormat(',', '_'))
2197 );
2198 assert_eq!(
2200 FormatSpec::parse(".,,f"),
2201 Err(FormatSpecError::InvalidFormatSpecifier)
2202 );
2203 assert_eq!(
2204 FormatSpec::parse(".__f"),
2205 Err(FormatSpecError::InvalidFormatSpecifier)
2206 );
2207 assert_eq!(
2209 FormatSpec::parse("."),
2210 Err(FormatSpecError::PrecisionMissing)
2211 );
2212 assert_eq!(
2213 FormatSpec::parse(".f"),
2214 Err(FormatSpecError::PrecisionMissing)
2215 );
2216 assert_eq!(
2218 FormatSpec::parse(".6,n").unwrap().format_float(1234.5678),
2219 Err(FormatSpecError::UnspecifiedFormat(',', 'n'))
2220 );
2221 assert_eq!(
2222 FormatSpec::parse("._n").unwrap().format_float(1234.5678),
2223 Err(FormatSpecError::UnspecifiedFormat('_', 'n'))
2224 );
2225 assert_eq!(
2227 FormatSpec::parse("_.6,n").unwrap().format_float(1234.5678),
2228 Err(FormatSpecError::UnspecifiedFormat('_', 'n'))
2229 );
2230 let locale = LocaleInfo {
2232 thousands_sep: ",".to_owned(),
2233 decimal_point: ".".to_owned(),
2234 grouping: vec![3, 0],
2235 };
2236 assert_eq!(
2237 FormatSpec::parse(".6,n")
2238 .unwrap()
2239 .format_complex_locale(&Complex64::new(1.0, 2.345678), &locale),
2240 Err(FormatSpecError::UnspecifiedFormat(',', 'n'))
2241 );
2242 }
2243
2244 #[test]
2245 fn format_float_empty_type_with_precision() {
2246 assert_eq!(fmt_float(".2", 1.0), "1.0");
2249 assert_eq!(fmt_float(".6", 100.0), "100.0");
2250 assert_eq!(fmt_float(".17", 1234567.0), "1234567.0");
2251 assert_eq!(fmt_float(".0", 0.5), "0.5");
2252 assert_eq!(fmt_float(".0", 0.0001), "0.0001");
2253 assert_eq!(fmt_float(".2", 0.0), "0.0");
2254 assert_eq!(fmt_float(".0", 0.0), "0e+00");
2255 assert_eq!(fmt_float(".2", 100.0), "1e+02");
2256 }
2257
2258 #[test]
2259 fn format_float_alternate_form_forces_point() {
2260 assert_eq!(fmt_float("#", 1e16), "1.e+16");
2262 assert_eq!(fmt_float("#", 1e-5), "1.e-05");
2263 assert_eq!(fmt_float("#", 100.0), "100.0");
2265 assert_eq!(fmt_float("#", 1.5), "1.5");
2266 }
2267
2268 #[test]
2269 fn format_int_hex_grouping_preserved() {
2270 assert_eq!(
2273 FormatSpec::parse("_x")
2274 .unwrap()
2275 .format_int(&BigInt::from(1000000))
2276 .unwrap(),
2277 "f_4240"
2278 );
2279 assert_eq!(
2280 FormatSpec::parse("_X")
2281 .unwrap()
2282 .format_int(&BigInt::from(0xABCDEFu32))
2283 .unwrap(),
2284 "AB_CDEF"
2285 );
2286 }
2287
2288 #[test]
2289 fn format_int_character_rejects_precision() {
2290 assert_eq!(
2292 FormatSpec::parse(".2c")
2293 .unwrap()
2294 .format_int(&BigInt::from(65)),
2295 Err(FormatSpecError::PrecisionNotAllowed)
2296 );
2297 assert_eq!(
2298 FormatSpec::parse("+.2c")
2299 .unwrap()
2300 .format_int(&BigInt::from(65)),
2301 Err(FormatSpecError::PrecisionNotAllowed)
2302 );
2303 assert_eq!(
2305 FormatSpec::parse("c")
2306 .unwrap()
2307 .format_int(&BigInt::from(65)),
2308 Ok("A".to_owned())
2309 );
2310 }
2311
2312 #[test]
2313 fn format_parse() {
2314 let expected = Ok(FormatString {
2315 format_parts: vec![
2316 FormatPart::Literal("abcd".into()),
2317 FormatPart::Field {
2318 field_name: "1".into(),
2319 conversion_spec: None,
2320 format_spec: "".into(),
2321 },
2322 FormatPart::Literal(":".into()),
2323 FormatPart::Field {
2324 field_name: "key".into(),
2325 conversion_spec: None,
2326 format_spec: "".into(),
2327 },
2328 ],
2329 });
2330
2331 assert_eq!(FormatString::from_str("abcd{1}:{key}".as_ref()), expected);
2332 }
2333
2334 #[test]
2335 fn format_parse_multi_byte_char() {
2336 assert!(FormatString::from_str("{a:%ЫйЯЧ}".as_ref()).is_ok());
2337 }
2338
2339 #[test]
2340 fn format_parse_fail() {
2341 assert_eq!(
2342 FormatString::from_str("{s".as_ref()),
2343 Err(FormatParseError::UnmatchedBracket)
2344 );
2345 }
2346
2347 #[test]
2348 fn format_parse_lone_start_bracket() {
2349 for lone in ["{", "a{"] {
2352 assert_eq!(
2353 FormatString::from_str(lone.as_ref()),
2354 Err(FormatParseError::UnescapedStartBracketInLiteral),
2355 "{lone:?}"
2356 );
2357 }
2358 for unclosed in ["{s", "a{b", "{0"] {
2359 assert_eq!(
2360 FormatString::from_str(unclosed.as_ref()),
2361 Err(FormatParseError::UnmatchedBracket),
2362 "{unclosed:?}"
2363 );
2364 }
2365 }
2366
2367 #[test]
2368 fn square_brackets_inside_format() {
2369 assert_eq!(
2370 FormatString::from_str("{[:123]}".as_ref()),
2371 Ok(FormatString {
2372 format_parts: vec![FormatPart::Field {
2373 field_name: "[:123]".into(),
2374 conversion_spec: None,
2375 format_spec: "".into(),
2376 }],
2377 }),
2378 );
2379
2380 assert_eq!(FormatString::from_str("{asdf[:123]asdf}".as_ref()), {
2381 Ok(FormatString {
2382 format_parts: vec![FormatPart::Field {
2383 field_name: "asdf[:123]asdf".into(),
2384 conversion_spec: None,
2385 format_spec: "".into(),
2386 }],
2387 })
2388 });
2389
2390 assert_eq!(FormatString::from_str("{[1234}".as_ref()), {
2391 Err(FormatParseError::MissingRightBracket)
2392 });
2393 }
2394
2395 #[test]
2396 fn format_parse_escape() {
2397 let expected = Ok(FormatString {
2398 format_parts: vec![
2399 FormatPart::Literal("{".into()),
2400 FormatPart::Field {
2401 field_name: "key".into(),
2402 conversion_spec: None,
2403 format_spec: "".into(),
2404 },
2405 FormatPart::Literal("}ddfe".into()),
2406 ],
2407 });
2408
2409 assert_eq!(FormatString::from_str("{{{key}}}ddfe".as_ref()), expected);
2410 }
2411
2412 #[test]
2413 fn format_invalid_specification() {
2414 assert_eq!(
2415 FormatSpec::parse("%3"),
2416 Err(FormatSpecError::InvalidFormatSpecifier)
2417 );
2418 assert_eq!(
2419 FormatSpec::parse(".2fa"),
2420 Err(FormatSpecError::InvalidFormatSpecifier)
2421 );
2422 assert_eq!(
2423 FormatSpec::parse("ds"),
2424 Err(FormatSpecError::InvalidFormatSpecifier)
2425 );
2426 assert_eq!(
2427 FormatSpec::parse("x+"),
2428 Err(FormatSpecError::InvalidFormatSpecifier)
2429 );
2430 assert_eq!(
2431 FormatSpec::parse("b4"),
2432 Err(FormatSpecError::InvalidFormatSpecifier)
2433 );
2434 assert_eq!(
2435 FormatSpec::parse("o!"),
2436 Err(FormatSpecError::InvalidFormatSpecifier)
2437 );
2438 assert_eq!(
2439 FormatSpec::parse("d "),
2440 Err(FormatSpecError::InvalidFormatSpecifier)
2441 );
2442 }
2443
2444 #[test]
2445 fn parse_field_name() {
2446 let parse = |s: &str| FieldName::parse(s.as_ref());
2447 assert_eq!(
2448 parse(""),
2449 Ok(FieldName {
2450 field_type: FieldType::Auto,
2451 parts: Vec::new(),
2452 })
2453 );
2454 assert_eq!(
2455 parse("0"),
2456 Ok(FieldName {
2457 field_type: FieldType::Index(0),
2458 parts: Vec::new(),
2459 })
2460 );
2461 assert_eq!(
2462 parse("key"),
2463 Ok(FieldName {
2464 field_type: FieldType::Keyword("key".into()),
2465 parts: Vec::new(),
2466 })
2467 );
2468 assert_eq!(
2469 parse("key.attr[0][string]"),
2470 Ok(FieldName {
2471 field_type: FieldType::Keyword("key".into()),
2472 parts: vec![
2473 FieldNamePart::Attribute("attr".into()),
2474 FieldNamePart::Index(0),
2475 FieldNamePart::StringIndex("string".into())
2476 ],
2477 })
2478 );
2479 assert_eq!(parse("key.."), Err(FormatParseError::EmptyAttribute));
2480 assert_eq!(parse("key[]"), Err(FormatParseError::EmptyAttribute));
2481 assert_eq!(parse("key["), Err(FormatParseError::MissingRightBracket));
2482 assert_eq!(
2483 parse("key[0]after"),
2484 Err(FormatParseError::InvalidCharacterAfterRightBracket)
2485 );
2486 }
2487}