1#![doc = include_str!("../readme.md")]
2
3pub use pure_rust_locales::Locale;
4
5use pure_rust_locales::locale_match;
6#[cfg(feature = "rust_decimal")]
7use rust_decimal::Decimal;
8use std::fmt;
9use std::fmt::{Debug, Display, Error as FmtError, Formatter, LowerExp, Write as FmtWrite};
10use std::str::{from_utf8_unchecked, FromStr};
11
12#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14pub struct NumberSymbols {
15 pub decimal_sep: char,
17 pub decimal_grp: Option<char>,
19 pub negative_sym: char,
21 pub positive_sym: char,
23 pub exponent_upper_sym: char,
25 pub exponent_lower_sym: char,
27 pub currency_sym: CurrencySym,
29 }
31
32impl Default for NumberSymbols {
33 fn default() -> Self {
34 Self::new()
35 }
36}
37
38impl NumberSymbols {
39 pub const fn new() -> Self {
40 Self {
41 decimal_sep: '.',
42 decimal_grp: Some(','),
43 negative_sym: '-',
44 positive_sym: ' ',
45 exponent_upper_sym: 'E',
46 exponent_lower_sym: 'e',
47 currency_sym: CurrencySym::new("$"),
48 }
49 }
50
51 pub fn numeric(locale: Locale) -> Self {
59 Self {
60 decimal_sep: first_or(locale_match!(locale => LC_NUMERIC::DECIMAL_POINT), '.'),
61 decimal_grp: first_opt(locale_match!(locale => LC_NUMERIC::THOUSANDS_SEP)),
62 negative_sym: '-',
63 positive_sym: ' ',
64 exponent_upper_sym: 'E',
65 exponent_lower_sym: 'e',
66 currency_sym: CurrencySym::new("$"),
67 }
68 }
69
70 pub fn monetary(locale: Locale) -> Self {
81 Self {
82 decimal_sep: first_or(locale_match!(locale => LC_MONETARY::MON_DECIMAL_POINT), '.'),
83 decimal_grp: first_opt(locale_match!(locale => LC_MONETARY::MON_THOUSANDS_SEP)),
84 negative_sym: first_or(locale_match!(locale => LC_MONETARY::NEGATIVE_SIGN), '-'),
85 positive_sym: first_or(locale_match!(locale => LC_MONETARY::POSITIVE_SIGN), ' '),
86 exponent_upper_sym: 'E',
87 exponent_lower_sym: 'e',
88 currency_sym: CurrencySym::new(locale_match!(locale => LC_MONETARY::CURRENCY_SYMBOL)),
89 }
90 }
91
92 pub fn int_monetary(locale: Locale) -> Self {
103 Self {
104 decimal_sep: first_or(locale_match!(locale => LC_MONETARY::MON_DECIMAL_POINT), '.'),
105 decimal_grp: first_opt(locale_match!(locale => LC_MONETARY::MON_THOUSANDS_SEP)),
106 negative_sym: first_or(locale_match!(locale => LC_MONETARY::NEGATIVE_SIGN), '-'),
107 positive_sym: first_or(locale_match!(locale => LC_MONETARY::POSITIVE_SIGN), ' '),
108 exponent_upper_sym: 'E',
109 exponent_lower_sym: 'e',
110 currency_sym: CurrencySym::new(locale_match!(locale => LC_MONETARY::INT_CURR_SYMBOL)),
111 }
112 }
113}
114
115#[inline]
117fn first_or(s: &str, default: char) -> char {
118 s.chars().next().unwrap_or(default)
119}
120
121#[inline]
123fn first_opt(s: &str) -> Option<char> {
124 s.chars().next()
125}
126
127#[derive(PartialEq, Eq, Clone, Copy)]
130pub struct CurrencySym {
131 len: u8,
132 sym: [u8; 16],
133}
134
135impl Debug for CurrencySym {
136 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
137 f.debug_struct("CurrencySym")
138 .field("len", &self.len)
139 .field("sym", &self.as_str())
140 .finish()
141 }
142}
143
144impl CurrencySym {
145 pub const fn new(src: &str) -> Self {
147 let mut sym = [0u8; 16];
148
149 let src = src.as_bytes();
150 let src_len = src.len();
151
152 let mut i = 0;
153 while i < src_len && i < 16 {
154 sym[i] = src[i];
155 i += 1;
156 }
157
158 CurrencySym {
159 len: src_len as u8,
160 sym,
161 }
162 }
163
164 pub fn as_str(&self) -> &str {
166 unsafe { from_utf8_unchecked(&self.sym[..self.len as usize]) }
169 }
170
171 pub const fn len(&self) -> usize {
173 self.len as usize
174 }
175
176 pub const fn is_empty(&self) -> bool {
178 self.len == 0
179 }
180}
181
182impl Display for CurrencySym {
183 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
184 write!(f, "{}", self.as_str())
185 }
186}
187
188impl<'a> From<&'a str> for CurrencySym {
189 fn from(value: &'a str) -> Self {
190 CurrencySym::new(value)
191 }
192}
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196enum Mode {
197 Integer,
198 Fraction,
199 Exponent,
200}
201
202#[allow(variant_size_differences)]
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213enum Token {
214 Digit0(Mode, u32),
216 Digit(Mode, u32),
218 Numeric(Mode, u32, bool),
220 SignInt,
222 PlusInt,
224 DecimalSep,
226 DecimalSepAlways,
228 GroupingSep(u32, bool),
230 ExponentUpper,
232 ExponentLower,
234 SignExp,
236 PlusExp,
238 Currency,
240 Separator(char),
242}
243
244#[derive(Debug, Default, Clone, PartialEq, Eq)]
246pub struct NumberFormat {
247 min_int_sign: u32,
249 len_int: u32,
251
252 has_exp: bool,
254 has_exp_0: bool,
256 min_exp_sign: u32,
258 len_exp: u32,
260
261 has_frac_0: bool,
263 len_frac: u8,
265
266 tok: Vec<Token>,
268 sym: NumberSymbols,
270}
271
272#[derive(Debug, PartialEq, Eq, Clone, Copy)]
274pub enum NumberFmtError {
275 Fmt,
277 FmtLenInt,
279 FmtLenExp,
281 FmtNoSign,
283 FmtNoExpSign,
285 Parse,
287 ParseInvalidDecimalSep,
289 ParseInvalidSign,
291 ParseInvalidExp,
293 ParseInvalidExpSign,
295 ParseUnescaped,
297 ParseInvalidDigit,
299 ParseInvalidGroupingSep,
301 ParseInvalidCurrency,
303 ParseInvalidSeparator,
305}
306
307impl std::error::Error for NumberFmtError {}
308
309impl Display for NumberFmtError {
310 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
311 write!(f, "{:?}", self)
312 }
313}
314
315impl From<FmtError> for NumberFmtError {
316 fn from(_: FmtError) -> Self {
317 NumberFmtError::Fmt
318 }
319}
320
321impl Display for NumberFormat {
322 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
323 for t in &self.tok {
324 match t {
325 Token::Digit0(_, _) => write!(f, "0")?,
326 Token::Digit(_, _) => write!(f, "9")?,
327 Token::Numeric(_, _, _) => write!(f, "#")?,
328 Token::SignInt => write!(f, "-")?,
329 Token::PlusInt => write!(f, "-")?,
330 Token::DecimalSep => write!(f, ".")?,
331 Token::DecimalSepAlways => write!(f, ":")?,
332 Token::GroupingSep(_, _) => write!(f, ",")?,
333 Token::ExponentUpper => write!(f, "E")?,
334 Token::ExponentLower => write!(f, "e")?,
335 Token::SignExp => write!(f, "-")?,
336 Token::PlusExp => write!(f, "+")?,
337 Token::Currency => write!(f, "$")?,
338 Token::Separator(c) => {
339 if *c < '\u{0100}' {
340 write!(f, "\\ ")?;
341 }
342 write!(f, "{}", *c)?;
343 }
344 }
345 }
346 Ok(())
347 }
348}
349
350impl NumberFormat {
351 pub fn new<S: AsRef<str>>(pattern: S) -> Result<Self, NumberFmtError> {
353 let tok = Self::parse_tokens(pattern.as_ref())?;
354 Self::news_tok(tok, NumberSymbols::new())
355 }
356
357 pub fn news<S: AsRef<str>>(pattern: S, sym: NumberSymbols) -> Result<Self, NumberFmtError> {
359 let tok = Self::parse_tokens(pattern.as_ref())?;
360 Self::news_tok(tok, sym)
361 }
362
363 fn news_tok(mut pattern: Vec<Token>, sym: NumberSymbols) -> Result<Self, NumberFmtError> {
365 let mut has_exp = false;
366 let mut has_exp_0 = false;
367 let mut has_dec_sep = false;
368 let mut has_frac_0 = false;
369 let mut has_int_sign = false;
370 let mut min_int_sign = 0;
371 let mut has_exp_sign = false;
372 let mut min_exp_sign = 0;
373 let mut len_frac = 0;
374 let mut len_int = 0;
375 let mut len_exp = 0;
376
377 let mut idx_frac = 0;
378 for t in pattern.iter_mut() {
379 match t {
380 Token::DecimalSep | Token::DecimalSepAlways => {
381 if has_dec_sep {
382 return Err(NumberFmtError::ParseInvalidDecimalSep);
383 }
384 has_dec_sep = true;
385 }
386 Token::Digit0(Mode::Fraction, x) => {
387 has_frac_0 = true;
388 len_frac += 1;
389 *x = idx_frac;
390 idx_frac += 1;
391 }
392 Token::Digit(Mode::Fraction, x) => {
393 len_frac += 1;
394 *x = idx_frac;
395 idx_frac += 1;
396 }
397 Token::Numeric(Mode::Fraction, x, sign) => {
398 len_frac += 1;
399 *x = idx_frac;
400 *sign = false;
401 idx_frac += 1;
402 }
403
404 Token::ExponentLower | Token::ExponentUpper => {
405 if has_exp {
406 return Err(NumberFmtError::ParseInvalidExp);
407 }
408 has_exp = true;
409 }
410
411 Token::SignInt => {
412 if has_int_sign {
413 return Err(NumberFmtError::ParseInvalidSign);
414 }
415 has_int_sign = true;
416 }
417 Token::PlusInt => {
418 if has_int_sign {
419 return Err(NumberFmtError::ParseInvalidSign);
420 }
421 has_int_sign = true;
422 }
423 Token::SignExp => {
424 if has_exp_sign {
425 return Err(NumberFmtError::ParseInvalidExpSign);
426 }
427 has_exp_sign = true;
428 }
429 Token::PlusExp => {
430 if has_exp_sign {
431 return Err(NumberFmtError::ParseInvalidExpSign);
432 }
433 has_exp_sign = true;
434 }
435
436 _ => {}
437 }
438 }
439 let mut idx_int = 0;
440 let mut idx_exp = 0;
441 let mut was_grp = false;
442 for t in pattern.iter_mut().rev() {
443 match t {
444 Token::Digit0(Mode::Integer, x) => {
445 len_int += 1;
446 min_int_sign = idx_int + 1;
447 *x = idx_int;
448 idx_int += 1;
449 }
450 Token::Digit(Mode::Integer, x) => {
451 len_int += 1;
452 min_int_sign = idx_int + 1;
453 *x = idx_int;
454 idx_int += 1;
455 }
456 Token::Numeric(Mode::Integer, x, sign) => {
457 len_int += 1;
458 *x = idx_int;
459 *sign = !has_int_sign && (sym.decimal_grp.is_none() || !was_grp);
460 idx_int += 1;
461 }
462
463 Token::GroupingSep(x, sign) => {
464 *sign = !has_int_sign;
465 *x = idx_int;
466 }
467
468 Token::Digit0(Mode::Exponent, x) => {
469 len_exp += 1;
470 has_exp_0 = true;
471 min_exp_sign = idx_exp + 1;
472 *x = idx_exp;
473 idx_exp += 1;
474 }
475 Token::Digit(Mode::Exponent, x) => {
476 len_exp += 1;
477 min_exp_sign = idx_exp;
478 *x = idx_exp;
479 idx_exp += 1;
480 }
481 Token::Numeric(Mode::Exponent, x, sign) => {
482 len_exp += 1;
483 *x = idx_exp;
484 *sign = !has_exp_sign;
485 idx_exp += 1;
486 }
487
488 _ => {}
489 }
490
491 was_grp = matches!(t, Token::GroupingSep(_, _));
492 }
493
494 Ok(NumberFormat {
495 min_int_sign,
496 len_int,
497 min_exp_sign,
498 has_exp,
499 len_exp,
500 has_exp_0,
501 has_frac_0,
502 len_frac,
503 tok: pattern,
504 sym,
505 })
506 }
507
508 fn parse_tokens(pattern: &str) -> Result<Vec<Token>, NumberFmtError> {
510 let mut esc = false;
511 let mut mode = Mode::Integer;
512
513 let mut tok = Vec::new();
514
515 for m in pattern.chars() {
516 let mask = if esc {
517 esc = false;
518 Token::Separator(m)
519 } else {
520 match m {
521 '0' => Token::Digit0(mode, 0),
522 '9' => Token::Digit(mode, 0),
523 '#' => Token::Numeric(mode, 0, false),
524 '.' => {
525 if matches!(mode, Mode::Fraction | Mode::Exponent) {
526 return Err(NumberFmtError::ParseInvalidDecimalSep);
527 }
528 mode = Mode::Fraction;
529 Token::DecimalSep
530 }
531 ':' => {
532 if matches!(mode, Mode::Fraction | Mode::Exponent) {
533 return Err(NumberFmtError::ParseInvalidDecimalSep);
534 }
535 mode = Mode::Fraction;
536 Token::DecimalSepAlways
537 }
538 ',' => Token::GroupingSep(0, false),
539 '-' => {
540 if mode == Mode::Integer {
541 Token::SignInt
542 } else if mode == Mode::Exponent {
543 Token::SignExp
544 } else {
545 return Err(NumberFmtError::ParseInvalidSign);
546 }
547 }
548 '+' => {
549 if mode == Mode::Integer {
550 Token::PlusInt
551 } else if mode == Mode::Exponent {
552 Token::PlusExp
553 } else {
554 return Err(NumberFmtError::ParseInvalidSign);
555 }
556 }
557 'e' => {
558 if mode == Mode::Exponent {
559 return Err(NumberFmtError::ParseInvalidExp);
560 }
561 mode = Mode::Exponent;
562 Token::ExponentLower
563 }
564 'E' => {
565 if mode == Mode::Exponent {
566 return Err(NumberFmtError::ParseInvalidExp);
567 }
568 mode = Mode::Exponent;
569 Token::ExponentUpper
570 }
571 '$' => Token::Currency,
572 '\\' => {
573 esc = true;
574 continue;
575 }
576 ' ' => Token::Separator(' '),
577 c if c.is_ascii() => return Err(NumberFmtError::ParseUnescaped),
578 c => Token::Separator(c),
579 }
580 };
581 tok.push(mask);
582 }
583
584 Ok(tok)
585 }
586
587 pub fn sym(&self) -> &NumberSymbols {
589 &self.sym
590 }
591
592 #[inline]
596 pub fn fmt_u<Number: LowerExp + Display>(&self, number: Number) -> String {
597 let mut out = String::new();
598 match core::format_to(number, self, self.sym(), &mut out) {
599 Ok(_) => {}
600 Err(e) => {
601 out.clear();
602 _ = write!(out, "{:?}", e);
603 }
604 }
605 out
606 }
607
608 #[inline]
610 pub fn fmt<Number: LowerExp + Display>(
611 &self,
612 number: Number,
613 ) -> Result<String, NumberFmtError> {
614 let mut out = String::new();
615 core::format_to(number, self, self.sym(), &mut out)?;
616 Ok(out)
617 }
618
619 #[inline]
621 pub fn fmt_to<Number: LowerExp + Display, W: FmtWrite>(
622 &self,
623 number: Number,
624 out: &mut W,
625 ) -> Result<(), NumberFmtError> {
626 core::format_to(number, self, self.sym(), out)
627 }
628
629 #[inline]
632 pub fn parse<F: FromStr>(&self, s: &str) -> Result<F, NumberFmtError> {
633 core::parse_fmt(s, self, &self.sym)
634 }
635}
636
637pub trait ParseNumber {
639 fn parse_sym<F: FromStr>(&self, sym: &NumberSymbols) -> Result<F, NumberFmtError>;
643 fn parse_fmt<F: FromStr>(&self, fmt: &NumberFormat) -> Result<F, NumberFmtError>;
646}
647
648impl ParseNumber for &str {
649 fn parse_sym<F: FromStr>(&self, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
650 core::parse_sym(self, sym)
651 }
652
653 fn parse_fmt<F: FromStr>(&self, fmt: &NumberFormat) -> Result<F, NumberFmtError> {
654 core::parse_fmt(self, fmt, &fmt.sym)
655 }
656}
657
658pub trait DisplayNumber
660where
661 Self: Copy + LowerExp + Display,
662{
663 fn format<'a>(
665 &self,
666 pattern: &'a str,
667 sym: &'a NumberSymbols,
668 ) -> Result<FormattedNumber<'a, Self>, NumberFmtError>;
669
670 fn fmt<'a>(&self, format: &'a NumberFormat) -> RefFormattedNumber<'a, Self>;
672}
673
674#[doc(hidden)]
677#[derive(Debug)]
678pub struct FormattedNumber<'a, Number> {
679 num: Number,
680 format: NumberFormat,
681 sym: &'a NumberSymbols,
682}
683
684impl<'a, Number: Copy + LowerExp + Display> Display for FormattedNumber<'a, Number> {
685 #[inline]
686 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
687 match core::format_to(self.num, &self.format, self.sym, f) {
688 Ok(_) => Ok(()),
689 Err(_) => Err(fmt::Error),
690 }
691 }
692}
693
694#[doc(hidden)]
697#[derive(Debug)]
698pub struct RefFormattedNumber<'a, Number> {
699 num: Number,
700 format: &'a NumberFormat,
701}
702
703impl<'a, Number: Copy + LowerExp + Display> Display for RefFormattedNumber<'a, Number> {
704 #[inline]
705 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
706 match core::format_to(self.num, self.format, &self.format.sym, f) {
707 Ok(_) => Ok(()),
708 Err(_) => Err(fmt::Error),
709 }
710 }
711}
712
713macro_rules! define_fmt {
714 ($t:ty) => {
715 impl DisplayNumber for $t {
716 #[inline]
717 fn format<'a>(
718 &self,
719 pattern: &'a str,
720 sym: &'a NumberSymbols,
721 ) -> Result<FormattedNumber<'a, Self>, NumberFmtError> {
722 Ok(FormattedNumber {
723 num: *self,
724 format: NumberFormat::new(pattern)?,
725 sym,
726 })
727 }
728
729 #[inline]
730 fn fmt<'a>(&self, format: &'a NumberFormat) -> RefFormattedNumber<'a, Self> {
731 RefFormattedNumber { num: *self, format }
732 }
733 }
734 };
735}
736
737define_fmt!(f64);
738define_fmt!(f32);
739define_fmt!(u128);
740define_fmt!(u64);
741define_fmt!(u32);
742define_fmt!(u16);
743define_fmt!(u8);
744define_fmt!(i128);
745define_fmt!(i64);
746define_fmt!(i32);
747define_fmt!(i16);
748define_fmt!(i8);
749define_fmt!(usize);
750define_fmt!(isize);
751#[cfg(feature = "rust_decimal")]
752define_fmt!(Decimal);
753
754pub mod core {
755 use crate::{Mode, NumberFmtError, NumberFormat, NumberSymbols, Token};
756 #[allow(unused_imports)]
757 use log::debug;
758 use memchr::memchr;
759 use std::cell::Cell;
760 use std::cmp::max;
761 use std::fmt::{Display, LowerExp, Write as FmtWrite};
762 use std::str::FromStr;
763
764 fn split_num(value: &str) -> (&str, &str, &str, &str, &str) {
765 let bytes = value.as_bytes();
767 let len = bytes.len();
768
769 let idx_sep = memchr(b'.', bytes);
770 let idx_exp = memchr(b'e', bytes);
771
772 let digits_end = if let Some(idx_sep) = idx_sep {
773 idx_sep
774 } else if let Some(idx_exp) = idx_exp {
775 idx_exp
776 } else {
777 len
778 };
779
780 let fraction_end = if let Some(idx_exp) = idx_exp {
781 idx_exp
782 } else {
783 len
784 };
785
786 let (r_sign, r_digits) = if len > 0 && bytes[0] == b'-' {
787 (0usize..1usize, 1usize..digits_end)
788 } else {
789 (0usize..0usize, 0usize..digits_end)
790 };
791 let r_fraction = if let Some(idx_sep) = idx_sep {
792 idx_sep + 1..fraction_end
793 } else {
794 fraction_end..fraction_end
795 };
796 let (r_sign_exp, r_exp) = if let Some(idx_exp) = idx_exp {
797 if idx_exp + 1 < len && bytes[idx_exp + 1] == b'-' {
798 (idx_exp + 1..idx_exp + 2, idx_exp + 2..len)
799 } else {
800 (idx_exp + 1..idx_exp + 1, idx_exp + 1..len)
801 }
802 } else {
803 (len..len, len..len)
804 };
805
806 (
807 &value[r_sign],
808 &value[r_digits],
809 &value[r_fraction],
810 &value[r_sign_exp],
811 &value[r_exp],
812 )
813 }
814
815 pub fn clean_num<W: FmtWrite>(
823 formatted: &str,
824 sym: &NumberSymbols,
825 out: &mut W,
826 ) -> Result<(), NumberFmtError> {
827 let mut seen_non_0 = false;
828 for c in formatted.chars() {
829 if c.is_ascii_digit() {
830 seen_non_0 |= c != '0';
831 if seen_non_0 {
832 out.write_char(c)?;
833 }
834 } else if c == sym.negative_sym {
835 out.write_char('-')?;
836 } else if c == sym.positive_sym {
837 } else if c == '+' {
839 } else if c == sym.decimal_sep {
841 out.write_char('.')?;
842 } else if c == sym.exponent_lower_sym || c == sym.exponent_upper_sym {
843 out.write_char('e')?;
844 }
845 }
846 Ok(())
847 }
848
849 #[allow(clippy::if_same_then_else)]
851 pub fn unmap_num<W: FmtWrite>(
852 formatted: &str,
853 format: &NumberFormat,
854 sym: &NumberSymbols,
855 out: &mut W,
856 ) -> Result<(), NumberFmtError> {
857 let mut buf_sign = String::new();
858 let mut buf_int = String::new();
859 let mut buf_frac = String::new();
860 let mut buf_exp_sign = String::new();
861 let mut buf_exp = String::new();
862
863 let mut it = format.tok.iter();
864 let mut jt = formatted.chars();
865 loop {
866 let Some(t) = it.next() else {
867 break;
868 };
869 let Some(c) = jt.next() else {
870 break;
871 };
872
873 match t {
874 Token::SignInt => {
875 if c == sym.negative_sym {
876 buf_sign.push('-');
877 } else if c == sym.positive_sym {
878 } else {
880 return Err(NumberFmtError::ParseInvalidSign);
881 }
882 }
883 Token::PlusInt => {
884 if c == '-' {
885 buf_sign.push('-');
886 } else if c == '+' {
887 buf_sign.push('+');
888 } else {
889 return Err(NumberFmtError::ParseInvalidSign);
890 }
891 }
892 Token::Digit0(Mode::Integer, _) => {
893 if c.is_ascii_digit() {
894 buf_int.push(c);
895 } else {
896 return Err(NumberFmtError::ParseInvalidDigit);
897 }
898 }
899 Token::Digit(Mode::Integer, _) => {
900 if c.is_ascii_digit() {
901 buf_int.push(c);
902 } else if c == ' ' {
903 } else {
905 return Err(NumberFmtError::ParseInvalidDigit);
906 }
907 }
908 Token::Numeric(Mode::Integer, _, _) => {
909 if c.is_ascii_digit() {
910 buf_int.push(c);
911 } else if c == sym.negative_sym {
912 buf_sign.push('-');
913 } else if c == sym.positive_sym || c == ' ' {
914 } else {
916 return Err(NumberFmtError::ParseInvalidDigit);
917 }
918 }
919 Token::GroupingSep(_, _) => {
920 if let Some(decimal_grp) = sym.decimal_grp {
921 if c == decimal_grp {
922 } else if c == sym.negative_sym {
924 buf_sign.push('-');
925 } else if c == sym.positive_sym || c == ' ' {
926 } else {
928 return Err(NumberFmtError::ParseInvalidGroupingSep);
929 }
930 }
931 }
932 Token::DecimalSep => {
933 if c == sym.decimal_sep {
934 buf_frac.push('.');
935 } else if c == ' ' {
936 } else {
938 return Err(NumberFmtError::ParseInvalidDecimalSep);
939 }
940 }
941 Token::DecimalSepAlways => {
942 if c == sym.decimal_sep {
943 buf_frac.push('.');
944 } else {
945 return Err(NumberFmtError::ParseInvalidDecimalSep);
946 }
947 }
948 Token::Digit0(Mode::Fraction, _) => {
949 if c.is_ascii_digit() {
950 buf_frac.push(c);
951 } else {
952 return Err(NumberFmtError::ParseInvalidDigit);
953 }
954 }
955 Token::Digit(Mode::Fraction, _) => {
956 if c.is_ascii_digit() {
957 buf_frac.push(c);
958 } else if c == ' ' {
959 } else {
961 return Err(NumberFmtError::ParseInvalidDigit);
962 }
963 }
964 Token::Numeric(Mode::Fraction, _, _) => {
965 if c.is_ascii_digit() {
966 buf_frac.push(c);
967 } else {
968 return Err(NumberFmtError::ParseInvalidDigit);
969 }
970 }
971 Token::ExponentUpper => {
972 if c == sym.exponent_upper_sym {
973 } else if c == ' ' {
975 } else {
977 return Err(NumberFmtError::ParseInvalidExp);
978 }
979 }
980 Token::ExponentLower => {
981 if c == sym.exponent_lower_sym {
982 } else if c == ' ' {
984 } else {
986 return Err(NumberFmtError::ParseInvalidExp);
987 }
988 }
989 Token::Digit0(Mode::Exponent, _) => {
990 if c.is_ascii_digit() {
991 buf_exp.push(c);
992 } else {
993 return Err(NumberFmtError::ParseInvalidDigit);
994 }
995 }
996 Token::Digit(Mode::Exponent, _) => {
997 if c.is_ascii_digit() {
998 buf_exp.push(c);
999 } else if c == ' ' {
1000 } else {
1002 return Err(NumberFmtError::ParseInvalidDigit);
1003 }
1004 }
1005 Token::Numeric(Mode::Exponent, _, _) => {
1006 if c.is_ascii_digit() {
1007 buf_exp.push(c);
1008 } else if c == sym.negative_sym {
1009 buf_exp_sign.push('-');
1010 } else if c == sym.positive_sym || c == ' ' {
1011 } else {
1013 return Err(NumberFmtError::ParseInvalidDigit);
1014 }
1015 }
1016 Token::SignExp => {
1017 if c == sym.negative_sym {
1018 buf_exp_sign.push('-');
1019 } else if c == sym.positive_sym || c == '+' {
1020 } else {
1022 return Err(NumberFmtError::ParseInvalidExpSign);
1023 }
1024 }
1025 Token::PlusExp => {
1026 if c == '-' {
1027 buf_exp_sign.push('-');
1028 } else if c == '+' {
1029 } else {
1031 return Err(NumberFmtError::ParseInvalidExpSign);
1032 }
1033 }
1034
1035 Token::Currency => {
1036 let mut kt = sym.currency_sym.as_str().chars();
1037 let s = kt.next();
1038 if Some(c) != s {
1039 return Err(NumberFmtError::ParseInvalidCurrency);
1040 }
1041
1042 loop {
1043 match kt.next() {
1044 None => {
1045 break;
1046 }
1047 Some(s) => {
1048 let Some(c) = jt.next() else {
1049 return Err(NumberFmtError::ParseInvalidCurrency);
1050 };
1051 if c != s {
1052 return Err(NumberFmtError::ParseInvalidCurrency);
1053 }
1054 }
1055 }
1056 }
1057 }
1058
1059 Token::Separator(sep) => {
1060 if c == *sep {
1061 } else {
1063 return Err(NumberFmtError::ParseInvalidSeparator);
1064 }
1065 }
1066 }
1067 }
1068
1069 out.write_str(buf_sign.as_str())?;
1070 out.write_str(buf_int.as_str())?;
1071 out.write_str(buf_frac.as_str())?;
1072 if !buf_exp.is_empty() {
1073 out.write_char('e')?;
1074 }
1075 out.write_str(buf_exp_sign.as_str())?;
1076 out.write_str(buf_exp.as_str())?;
1077
1078 Ok(())
1079 }
1080
1081 #[inline]
1086 pub fn map_num<W: FmtWrite, const EXP: bool>(
1087 raw: &str,
1088 format: &NumberFormat,
1089 sym: &NumberSymbols,
1090 out: &mut W,
1091 ) -> Result<(), NumberFmtError> {
1092 let (raw_sign, raw_int, raw_frac, raw_exp_sign, raw_exp) = split_num(raw);
1093
1094 let skip_group = sym.decimal_grp.is_none();
1098 let disp_decimal_grp = if let Some(decimal_grp) = sym.decimal_grp {
1099 decimal_grp
1100 } else {
1101 ' '
1102 };
1103
1104 let disp_sign = if raw_sign.is_empty() {
1106 sym.positive_sym
1107 } else {
1108 sym.negative_sym
1109 };
1110
1111 let int = raw_int.as_bytes();
1113 let len_int = int.len() as u32;
1114 if len_int > format.len_int {
1115 return Err(NumberFmtError::FmtLenInt);
1116 }
1117
1118 let disp_decimal_sep = if !raw_frac.is_empty() || format.has_frac_0 {
1120 sym.decimal_sep
1121 } else {
1122 ' '
1123 };
1124
1125 let frac = raw_frac.as_bytes();
1127 let len_frac = frac.len() as u32;
1128
1129 let len_exp_sign = raw_exp_sign.len() as u32;
1131
1132 let exp = raw_exp.as_bytes();
1134 let len_exp = exp.len() as u32;
1135
1136 let (disp_exp_upper, disp_exp_lower, disp_exp_sign, shift_exp_n, shift_exp_pos) = if EXP {
1137 let disp_exp_upper = if !raw_exp.is_empty() || format.has_exp_0 {
1138 sym.exponent_upper_sym
1139 } else {
1140 ' '
1141 };
1142 let disp_exp_lower = if !raw_exp.is_empty() || format.has_exp_0 {
1143 sym.exponent_lower_sym
1144 } else {
1145 ' '
1146 };
1147 let disp_exp_sign = if raw_exp_sign.is_empty() {
1148 sym.positive_sym
1149 } else {
1150 sym.negative_sym
1151 };
1152
1153 if len_exp > format.len_exp {
1154 return Err(NumberFmtError::FmtLenExp);
1155 }
1156 if max(len_exp, format.min_exp_sign) + len_exp_sign > format.len_exp {
1158 return Err(NumberFmtError::FmtLenExp);
1159 }
1160 let shift_exp_n = format.len_exp - max(len_exp, format.min_exp_sign) - len_exp_sign;
1162 let shift_exp_pos = max(len_exp, format.min_exp_sign) + len_exp_sign;
1163
1164 (
1165 disp_exp_upper,
1166 disp_exp_lower,
1167 disp_exp_sign,
1168 shift_exp_n,
1169 shift_exp_pos,
1170 )
1171 } else {
1172 (' ', ' ', ' ', 0, 0)
1173 };
1174
1175 let mut used_sign = false;
1176 let mut used_exp_sign = false;
1177
1178 for m in format.tok.iter() {
1179 match m {
1180 Token::SignInt => {
1181 debug_assert!(!used_sign);
1182 out.write_char(disp_sign)?;
1183 used_sign = true;
1184 }
1185 Token::PlusInt => {
1186 debug_assert!(!used_sign);
1187 if raw_sign.is_empty() {
1188 out.write_char('+')?;
1189 } else {
1190 out.write_char('-')?;
1191 }
1192 }
1193 Token::GroupingSep(i, can_be_sign) => {
1194 if skip_group {
1195 } else if len_int > *i {
1197 out.write_char(disp_decimal_grp)?;
1198 } else if *can_be_sign && max(len_int, format.min_int_sign) == *i {
1199 debug_assert!(!used_sign);
1200 out.write_char(disp_sign)?;
1201 used_sign = true;
1202 } else {
1203 out.write_char(' ')?;
1204 }
1205 }
1206 Token::Digit0(Mode::Integer, i) => {
1207 if len_int > *i {
1208 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1209 } else {
1210 out.write_char('0')?;
1211 }
1212 }
1213 Token::Digit(Mode::Integer, i) => {
1214 if len_int > *i {
1215 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1216 } else {
1217 out.write_char(' ')?;
1218 }
1219 }
1220 Token::Numeric(Mode::Integer, i, can_be_sign) => {
1221 if len_int > *i {
1222 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1223 } else if *can_be_sign && max(len_int, format.min_int_sign) == *i {
1224 debug_assert!(!used_sign);
1225 out.write_char(disp_sign)?;
1226 used_sign = true;
1227 } else {
1228 out.write_char(' ')?;
1229 }
1230 }
1231 Token::DecimalSep => {
1232 out.write_char(disp_decimal_sep)?;
1233 }
1234 Token::DecimalSepAlways => {
1235 out.write_char(sym.decimal_sep)?;
1236 }
1237 Token::Digit0(Mode::Fraction, i) => {
1238 if len_frac > *i {
1239 out.write_char(frac[*i as usize] as char)?;
1240 } else {
1241 out.write_char('0')?;
1242 }
1243 }
1244 Token::Digit(Mode::Fraction, i) => {
1245 if len_frac > *i {
1246 out.write_char(frac[*i as usize] as char)?;
1247 } else {
1248 out.write_char(' ')?;
1249 }
1250 }
1251 Token::Numeric(Mode::Fraction, i, _) => {
1252 if len_frac > *i {
1253 out.write_char(frac[*i as usize] as char)?;
1254 } else {
1255 out.write_char(' ')?;
1256 }
1257 }
1258 Token::ExponentUpper => {
1259 if EXP {
1260 out.write_char(disp_exp_upper)?;
1261 }
1262 }
1263 Token::ExponentLower => {
1264 if EXP {
1265 out.write_char(disp_exp_lower)?;
1266 }
1267 }
1268 Token::SignExp => {
1269 if EXP {
1270 debug_assert!(!used_exp_sign);
1271 if raw_exp_sign.is_empty() && sym.positive_sym == ' ' {
1272 out.write_char('+')?;
1274 } else {
1275 out.write_char(disp_exp_sign)?;
1276 }
1277 used_exp_sign = true;
1278 }
1279 }
1280 Token::PlusExp => {
1281 if EXP {
1282 debug_assert!(!used_exp_sign);
1283 if raw_exp_sign.is_empty() {
1284 out.write_char('+')?;
1285 } else {
1286 out.write_char('-')?;
1287 }
1288 used_exp_sign = true;
1289 }
1290 }
1291 Token::Digit0(Mode::Exponent, i) => {
1292 if EXP {
1293 if *i >= shift_exp_pos {
1294 } else if len_exp > *i {
1296 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1297 } else {
1298 out.write_char('0')?;
1299 }
1300 if *i == 0 {
1302 for _ in 0..shift_exp_n {
1303 out.write_char(' ')?;
1304 }
1305 }
1306 }
1307 }
1308 Token::Digit(Mode::Exponent, i) => {
1309 if EXP {
1310 if *i >= shift_exp_pos {
1311 } else if len_exp > *i {
1313 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1314 } else {
1315 out.write_char(' ')?;
1316 }
1317 if *i == 0 {
1319 for _ in 0..shift_exp_n {
1320 out.write_char(' ')?;
1321 }
1322 }
1323 }
1324 }
1325 Token::Numeric(Mode::Exponent, i, can_be_sign) => {
1326 if EXP {
1327 if *i >= shift_exp_pos {
1328 } else if len_exp > *i {
1330 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1331 } else if *can_be_sign && max(len_exp, format.min_exp_sign) == *i {
1332 debug_assert!(!used_exp_sign);
1333 out.write_char(disp_exp_sign)?;
1334 used_exp_sign = true;
1335 } else {
1336 out.write_char(' ')?;
1337 }
1338
1339 if *i == 0 {
1341 for _ in 0..shift_exp_n {
1342 out.write_char(' ')?;
1343 }
1344 }
1345 }
1346 }
1347 Token::Currency => {
1348 out.write_str(sym.currency_sym.as_str())?;
1349 }
1350 Token::Separator(v) => {
1351 out.write_char(*v)?;
1352 }
1353 }
1354 }
1355
1356 if !used_sign && !raw_sign.is_empty() {
1357 return Err(NumberFmtError::FmtNoSign);
1358 }
1359 if !used_exp_sign && !raw_exp_sign.is_empty() {
1360 return Err(NumberFmtError::FmtNoExpSign);
1361 }
1362
1363 Ok(())
1364 }
1365
1366 pub fn format_to<W: FmtWrite, Number: LowerExp + Display>(
1368 number: Number,
1369 format: &NumberFormat,
1370 sym: &NumberSymbols,
1371 out: &mut W,
1372 ) -> Result<(), NumberFmtError> {
1373 thread_local! {
1374 static RAW: Cell<String> = const {Cell::new(String::new())};
1375 }
1376
1377 let mut raw = RAW.take();
1378
1379 raw.clear();
1380 let res = if format.has_exp {
1381 write!(raw, "{:.*e}", format.len_frac as usize, number)
1382 .map_err(|_| NumberFmtError::Fmt)?;
1383 map_num::<_, true>(raw.as_str(), format, sym, out)
1384 } else {
1385 write!(raw, "{:.*}", format.len_frac as usize, number)
1386 .map_err(|_| NumberFmtError::Fmt)?;
1387 map_num::<_, false>(raw.as_str(), format, sym, out)
1388 };
1389
1390 match res {
1391 Ok(v) => {
1392 RAW.set(raw);
1393 Ok(v)
1394 }
1395 Err(e) => {
1396 RAW.set(raw);
1397 Err(e)
1398 }
1399 }
1400 }
1401
1402 pub fn parse_fmt<F: FromStr>(
1404 s: &str,
1405 fmt: &NumberFormat,
1406 sym: &NumberSymbols,
1407 ) -> Result<F, NumberFmtError> {
1408 thread_local! {
1409 static RAW: Cell<String> = const {Cell::new(String::new())};
1410 }
1411
1412 let mut raw = RAW.take();
1413
1414 raw.clear();
1415 unmap_num(s, fmt, sym, &mut raw)?;
1416
1417 match raw.parse::<F>() {
1418 Ok(v) => {
1419 RAW.set(raw);
1420 Ok(v)
1421 }
1422 Err(_) => {
1423 RAW.set(raw);
1424 Err(NumberFmtError::Parse)
1425 }
1426 }
1427 }
1428
1429 pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1432 thread_local! {
1433 static RAW: Cell<String> = const {Cell::new(String::new())};
1434 }
1435
1436 let mut raw = RAW.take();
1437
1438 raw.clear();
1439 clean_num(s, sym, &mut raw)?;
1440
1441 match raw.parse::<F>() {
1442 Ok(v) => {
1443 RAW.set(raw);
1444 Ok(v)
1445 }
1446 Err(_) => {
1447 RAW.set(raw);
1448 Err(NumberFmtError::Parse)
1449 }
1450 }
1451 }
1452}
1453
1454pub fn format<Number: LowerExp + Display>(
1457 number: Number,
1458 pattern: &str,
1459) -> Result<String, NumberFmtError> {
1460 let fmt = NumberFormat::new(pattern)?;
1461 let mut out = String::new();
1462 core::format_to(number, &fmt, fmt.sym(), &mut out)?;
1463 Ok(out)
1464}
1465
1466pub fn format_to<W: FmtWrite, Number: LowerExp + Display>(
1469 number: Number,
1470 pattern: &str,
1471 out: &mut W,
1472) -> Result<(), NumberFmtError> {
1473 let fmt = NumberFormat::new(pattern)?;
1474 core::format_to(number, &fmt, fmt.sym(), out)
1475}
1476
1477pub fn formats<Number: LowerExp + Display>(
1479 number: Number,
1480 pattern: &str,
1481 sym: &NumberSymbols,
1482) -> Result<String, NumberFmtError> {
1483 let format = NumberFormat::new(pattern)?;
1484 let mut out = String::new();
1485 core::format_to(number, &format, sym, &mut out)?;
1486 Ok(out)
1487}
1488
1489pub fn formats_to<W: FmtWrite, Number: LowerExp + Display>(
1491 number: Number,
1492 pattern: &str,
1493 sym: &NumberSymbols,
1494 out: &mut W,
1495) -> Result<(), NumberFmtError> {
1496 let format = NumberFormat::new(pattern)?;
1497 core::format_to(number, &format, sym, out)
1498}
1499
1500pub fn fmt<Number: LowerExp + Display>(number: Number, format: &NumberFormat) -> String {
1502 let mut out = String::new();
1503 _ = core::format_to(number, format, &format.sym, &mut out);
1504 out
1505}
1506
1507pub fn fmt_to<W: FmtWrite, Number: LowerExp + Display>(
1509 number: Number,
1510 format: &NumberFormat,
1511 out: &mut W,
1512) {
1513 _ = core::format_to(number, format, &format.sym, out)
1514}
1515
1516pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1519 core::parse_sym(s, sym)
1520}
1521
1522pub fn parse_fmt<F: FromStr>(s: &str, fmt: &NumberFormat) -> Result<F, NumberFmtError> {
1525 core::parse_fmt(s, fmt, &fmt.sym)
1526}
1527
1528pub fn parse_format<F: FromStr>(
1531 s: &str,
1532 pattern: &str,
1533 sym: &NumberSymbols,
1534) -> Result<F, NumberFmtError> {
1535 let format = NumberFormat::new(pattern)?;
1536 core::parse_fmt(s, &format, sym)
1537}