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 if c == ' ' {
968 } else {
970 return Err(NumberFmtError::ParseInvalidDigit);
971 }
972 }
973 Token::ExponentUpper => {
974 if c == sym.exponent_upper_sym {
975 } else if c == ' ' {
977 } else {
979 return Err(NumberFmtError::ParseInvalidExp);
980 }
981 }
982 Token::ExponentLower => {
983 if c == sym.exponent_lower_sym {
984 } else if c == ' ' {
986 } else {
988 return Err(NumberFmtError::ParseInvalidExp);
989 }
990 }
991 Token::Digit0(Mode::Exponent, _) => {
992 if c.is_ascii_digit() {
993 buf_exp.push(c);
994 } else {
995 return Err(NumberFmtError::ParseInvalidDigit);
996 }
997 }
998 Token::Digit(Mode::Exponent, _) => {
999 if c.is_ascii_digit() {
1000 buf_exp.push(c);
1001 } else if c == ' ' {
1002 } else {
1004 return Err(NumberFmtError::ParseInvalidDigit);
1005 }
1006 }
1007 Token::Numeric(Mode::Exponent, _, _) => {
1008 if c.is_ascii_digit() {
1009 buf_exp.push(c);
1010 } else if c == sym.negative_sym {
1011 buf_exp_sign.push('-');
1012 } else if c == sym.positive_sym || c == ' ' {
1013 } else {
1015 return Err(NumberFmtError::ParseInvalidDigit);
1016 }
1017 }
1018 Token::SignExp => {
1019 if c == sym.negative_sym {
1020 buf_exp_sign.push('-');
1021 } else if c == sym.positive_sym || c == '+' {
1022 } else {
1024 return Err(NumberFmtError::ParseInvalidExpSign);
1025 }
1026 }
1027 Token::PlusExp => {
1028 if c == '-' {
1029 buf_exp_sign.push('-');
1030 } else if c == '+' {
1031 } else {
1033 return Err(NumberFmtError::ParseInvalidExpSign);
1034 }
1035 }
1036
1037 Token::Currency => {
1038 let mut kt = sym.currency_sym.as_str().chars();
1039 let s = kt.next();
1040 if Some(c) != s {
1041 return Err(NumberFmtError::ParseInvalidCurrency);
1042 }
1043
1044 loop {
1045 match kt.next() {
1046 None => {
1047 break;
1048 }
1049 Some(s) => {
1050 let Some(c) = jt.next() else {
1051 return Err(NumberFmtError::ParseInvalidCurrency);
1052 };
1053 if c != s {
1054 return Err(NumberFmtError::ParseInvalidCurrency);
1055 }
1056 }
1057 }
1058 }
1059 }
1060
1061 Token::Separator(sep) => {
1062 if c == *sep {
1063 } else {
1065 return Err(NumberFmtError::ParseInvalidSeparator);
1066 }
1067 }
1068 }
1069 }
1070
1071 out.write_str(buf_sign.as_str())?;
1072 out.write_str(buf_int.as_str())?;
1073 out.write_str(buf_frac.as_str())?;
1074 if !buf_exp.is_empty() {
1075 out.write_char('e')?;
1076 }
1077 out.write_str(buf_exp_sign.as_str())?;
1078 out.write_str(buf_exp.as_str())?;
1079
1080 Ok(())
1081 }
1082
1083 #[inline]
1088 pub fn map_num<W: FmtWrite, const EXP: bool>(
1089 raw: &str,
1090 format: &NumberFormat,
1091 sym: &NumberSymbols,
1092 out: &mut W,
1093 ) -> Result<(), NumberFmtError> {
1094 let (raw_sign, raw_int, raw_frac, raw_exp_sign, raw_exp) = split_num(raw);
1095
1096 let skip_group = sym.decimal_grp.is_none();
1100 let disp_decimal_grp = sym.decimal_grp.unwrap_or(' ');
1101
1102 let disp_sign = if raw_sign.is_empty() {
1104 sym.positive_sym
1105 } else {
1106 sym.negative_sym
1107 };
1108
1109 let int = raw_int.as_bytes();
1111 let len_int = int.len() as u32;
1112 if len_int > format.len_int {
1113 return Err(NumberFmtError::FmtLenInt);
1114 }
1115
1116 let disp_decimal_sep = if !raw_frac.is_empty() || format.has_frac_0 {
1118 sym.decimal_sep
1119 } else {
1120 ' '
1121 };
1122
1123 let frac = raw_frac.as_bytes();
1125 let len_frac = frac.len() as u32;
1126
1127 let len_exp_sign = raw_exp_sign.len() as u32;
1129
1130 let exp = raw_exp.as_bytes();
1132 let len_exp = exp.len() as u32;
1133
1134 let (disp_exp_upper, disp_exp_lower, disp_exp_sign, shift_exp_n, shift_exp_pos) = if EXP {
1135 let disp_exp_upper = if !raw_exp.is_empty() || format.has_exp_0 {
1136 sym.exponent_upper_sym
1137 } else {
1138 ' '
1139 };
1140 let disp_exp_lower = if !raw_exp.is_empty() || format.has_exp_0 {
1141 sym.exponent_lower_sym
1142 } else {
1143 ' '
1144 };
1145 let disp_exp_sign = if raw_exp_sign.is_empty() {
1146 sym.positive_sym
1147 } else {
1148 sym.negative_sym
1149 };
1150
1151 if len_exp > format.len_exp {
1152 return Err(NumberFmtError::FmtLenExp);
1153 }
1154 if max(len_exp, format.min_exp_sign) + len_exp_sign > format.len_exp {
1156 return Err(NumberFmtError::FmtLenExp);
1157 }
1158 let shift_exp_n = format.len_exp - max(len_exp, format.min_exp_sign) - len_exp_sign;
1160 let shift_exp_pos = max(len_exp, format.min_exp_sign) + len_exp_sign;
1161
1162 (
1163 disp_exp_upper,
1164 disp_exp_lower,
1165 disp_exp_sign,
1166 shift_exp_n,
1167 shift_exp_pos,
1168 )
1169 } else {
1170 (' ', ' ', ' ', 0, 0)
1171 };
1172
1173 let mut used_sign = false;
1174 let mut used_exp_sign = false;
1175
1176 for m in format.tok.iter() {
1177 match m {
1178 Token::SignInt => {
1179 debug_assert!(!used_sign);
1180 out.write_char(disp_sign)?;
1181 used_sign = true;
1182 }
1183 Token::PlusInt => {
1184 debug_assert!(!used_sign);
1185 if raw_sign.is_empty() {
1186 out.write_char('+')?;
1187 } else {
1188 out.write_char('-')?;
1189 }
1190 }
1191 Token::GroupingSep(i, can_be_sign) => {
1192 if skip_group {
1193 } else if len_int > *i {
1195 out.write_char(disp_decimal_grp)?;
1196 } else if *can_be_sign && max(len_int, format.min_int_sign) == *i {
1197 debug_assert!(!used_sign);
1198 out.write_char(disp_sign)?;
1199 used_sign = true;
1200 } else {
1201 out.write_char(' ')?;
1202 }
1203 }
1204 Token::Digit0(Mode::Integer, i) => {
1205 if len_int > *i {
1206 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1207 } else {
1208 out.write_char('0')?;
1209 }
1210 }
1211 Token::Digit(Mode::Integer, i) => {
1212 if len_int > *i {
1213 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1214 } else {
1215 out.write_char(' ')?;
1216 }
1217 }
1218 Token::Numeric(Mode::Integer, i, can_be_sign) => {
1219 if len_int > *i {
1220 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1221 } else if *can_be_sign && max(len_int, format.min_int_sign) == *i {
1222 debug_assert!(!used_sign);
1223 out.write_char(disp_sign)?;
1224 used_sign = true;
1225 } else {
1226 out.write_char(' ')?;
1227 }
1228 }
1229 Token::DecimalSep => {
1230 out.write_char(disp_decimal_sep)?;
1231 }
1232 Token::DecimalSepAlways => {
1233 out.write_char(sym.decimal_sep)?;
1234 }
1235 Token::Digit0(Mode::Fraction, i) => {
1236 if len_frac > *i {
1237 out.write_char(frac[*i as usize] as char)?;
1238 } else {
1239 out.write_char('0')?;
1240 }
1241 }
1242 Token::Digit(Mode::Fraction, i) => {
1243 if len_frac > *i {
1244 out.write_char(frac[*i as usize] as char)?;
1245 } else {
1246 out.write_char(' ')?;
1247 }
1248 }
1249 Token::Numeric(Mode::Fraction, i, _) => {
1250 if len_frac > *i {
1251 out.write_char(frac[*i as usize] as char)?;
1252 } else {
1253 out.write_char(' ')?;
1254 }
1255 }
1256 Token::ExponentUpper => {
1257 if EXP {
1258 out.write_char(disp_exp_upper)?;
1259 }
1260 }
1261 Token::ExponentLower => {
1262 if EXP {
1263 out.write_char(disp_exp_lower)?;
1264 }
1265 }
1266 Token::SignExp => {
1267 if EXP {
1268 debug_assert!(!used_exp_sign);
1269 if raw_exp_sign.is_empty() && sym.positive_sym == ' ' {
1270 out.write_char('+')?;
1272 } else {
1273 out.write_char(disp_exp_sign)?;
1274 }
1275 used_exp_sign = true;
1276 }
1277 }
1278 Token::PlusExp => {
1279 if EXP {
1280 debug_assert!(!used_exp_sign);
1281 if raw_exp_sign.is_empty() {
1282 out.write_char('+')?;
1283 } else {
1284 out.write_char('-')?;
1285 }
1286 used_exp_sign = true;
1287 }
1288 }
1289 Token::Digit0(Mode::Exponent, i) => {
1290 if EXP {
1291 if *i >= shift_exp_pos {
1292 } else if len_exp > *i {
1294 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1295 } else {
1296 out.write_char('0')?;
1297 }
1298 if *i == 0 {
1300 for _ in 0..shift_exp_n {
1301 out.write_char(' ')?;
1302 }
1303 }
1304 }
1305 }
1306 Token::Digit(Mode::Exponent, i) => {
1307 if EXP {
1308 if *i >= shift_exp_pos {
1309 } else if len_exp > *i {
1311 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1312 } else {
1313 out.write_char(' ')?;
1314 }
1315 if *i == 0 {
1317 for _ in 0..shift_exp_n {
1318 out.write_char(' ')?;
1319 }
1320 }
1321 }
1322 }
1323 Token::Numeric(Mode::Exponent, i, can_be_sign) => {
1324 if EXP {
1325 if *i >= shift_exp_pos {
1326 } else if len_exp > *i {
1328 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1329 } else if *can_be_sign && max(len_exp, format.min_exp_sign) == *i {
1330 debug_assert!(!used_exp_sign);
1331 out.write_char(disp_exp_sign)?;
1332 used_exp_sign = true;
1333 } else {
1334 out.write_char(' ')?;
1335 }
1336
1337 if *i == 0 {
1339 for _ in 0..shift_exp_n {
1340 out.write_char(' ')?;
1341 }
1342 }
1343 }
1344 }
1345 Token::Currency => {
1346 out.write_str(sym.currency_sym.as_str())?;
1347 }
1348 Token::Separator(v) => {
1349 out.write_char(*v)?;
1350 }
1351 }
1352 }
1353
1354 if !used_sign && !raw_sign.is_empty() {
1355 return Err(NumberFmtError::FmtNoSign);
1356 }
1357 if !used_exp_sign && !raw_exp_sign.is_empty() {
1358 return Err(NumberFmtError::FmtNoExpSign);
1359 }
1360
1361 Ok(())
1362 }
1363
1364 pub fn format_to<W: FmtWrite, Number: LowerExp + Display>(
1366 number: Number,
1367 format: &NumberFormat,
1368 sym: &NumberSymbols,
1369 out: &mut W,
1370 ) -> Result<(), NumberFmtError> {
1371 thread_local! {
1372 static RAW: Cell<String> = const {Cell::new(String::new())};
1373 }
1374
1375 let mut raw = RAW.take();
1376
1377 raw.clear();
1378 let res = if format.has_exp {
1379 write!(raw, "{:.*e}", format.len_frac as usize, number)
1380 .map_err(|_| NumberFmtError::Fmt)?;
1381 map_num::<_, true>(raw.as_str(), format, sym, out)
1382 } else {
1383 write!(raw, "{:.*}", format.len_frac as usize, number)
1384 .map_err(|_| NumberFmtError::Fmt)?;
1385 map_num::<_, false>(raw.as_str(), format, sym, out)
1386 };
1387
1388 match res {
1389 Ok(v) => {
1390 RAW.set(raw);
1391 Ok(v)
1392 }
1393 Err(e) => {
1394 RAW.set(raw);
1395 Err(e)
1396 }
1397 }
1398 }
1399
1400 pub fn parse_fmt<F: FromStr>(
1402 s: &str,
1403 fmt: &NumberFormat,
1404 sym: &NumberSymbols,
1405 ) -> Result<F, NumberFmtError> {
1406 thread_local! {
1407 static RAW: Cell<String> = const {Cell::new(String::new())};
1408 }
1409
1410 let mut raw = RAW.take();
1411
1412 raw.clear();
1413 unmap_num(s, fmt, sym, &mut raw)?;
1414
1415 match raw.parse::<F>() {
1416 Ok(v) => {
1417 RAW.set(raw);
1418 Ok(v)
1419 }
1420 Err(_) => {
1421 RAW.set(raw);
1422 Err(NumberFmtError::Parse)
1423 }
1424 }
1425 }
1426
1427 pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1430 thread_local! {
1431 static RAW: Cell<String> = const {Cell::new(String::new())};
1432 }
1433
1434 let mut raw = RAW.take();
1435
1436 raw.clear();
1437 clean_num(s, sym, &mut raw)?;
1438
1439 match raw.parse::<F>() {
1440 Ok(v) => {
1441 RAW.set(raw);
1442 Ok(v)
1443 }
1444 Err(_) => {
1445 RAW.set(raw);
1446 Err(NumberFmtError::Parse)
1447 }
1448 }
1449 }
1450}
1451
1452pub fn format<Number: LowerExp + Display>(
1455 number: Number,
1456 pattern: &str,
1457) -> Result<String, NumberFmtError> {
1458 let fmt = NumberFormat::new(pattern)?;
1459 let mut out = String::new();
1460 core::format_to(number, &fmt, fmt.sym(), &mut out)?;
1461 Ok(out)
1462}
1463
1464pub fn format_to<W: FmtWrite, Number: LowerExp + Display>(
1467 number: Number,
1468 pattern: &str,
1469 out: &mut W,
1470) -> Result<(), NumberFmtError> {
1471 let fmt = NumberFormat::new(pattern)?;
1472 core::format_to(number, &fmt, fmt.sym(), out)
1473}
1474
1475pub fn formats<Number: LowerExp + Display>(
1477 number: Number,
1478 pattern: &str,
1479 sym: &NumberSymbols,
1480) -> Result<String, NumberFmtError> {
1481 let format = NumberFormat::new(pattern)?;
1482 let mut out = String::new();
1483 core::format_to(number, &format, sym, &mut out)?;
1484 Ok(out)
1485}
1486
1487pub fn formats_to<W: FmtWrite, Number: LowerExp + Display>(
1489 number: Number,
1490 pattern: &str,
1491 sym: &NumberSymbols,
1492 out: &mut W,
1493) -> Result<(), NumberFmtError> {
1494 let format = NumberFormat::new(pattern)?;
1495 core::format_to(number, &format, sym, out)
1496}
1497
1498pub fn fmt<Number: LowerExp + Display>(number: Number, format: &NumberFormat) -> String {
1500 let mut out = String::new();
1501 _ = core::format_to(number, format, &format.sym, &mut out);
1502 out
1503}
1504
1505pub fn fmt_to<W: FmtWrite, Number: LowerExp + Display>(
1507 number: Number,
1508 format: &NumberFormat,
1509 out: &mut W,
1510) {
1511 _ = core::format_to(number, format, &format.sym, out)
1512}
1513
1514pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1517 core::parse_sym(s, sym)
1518}
1519
1520pub fn parse_fmt<F: FromStr>(s: &str, fmt: &NumberFormat) -> Result<F, NumberFmtError> {
1523 core::parse_fmt(s, fmt, &fmt.sym)
1524}
1525
1526pub fn parse_format<F: FromStr>(
1529 s: &str,
1530 pattern: &str,
1531 sym: &NumberSymbols,
1532) -> Result<F, NumberFmtError> {
1533 let format = NumberFormat::new(pattern)?;
1534 core::parse_fmt(s, &format, sym)
1535}