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>(
820 formatted: &str,
821 sym: &NumberSymbols,
822 out: &mut W,
823 ) -> Result<(), NumberFmtError> {
824 for c in formatted.chars() {
825 if c.is_ascii_digit() {
826 out.write_char(c)?;
827 } else if c == sym.negative_sym || c == '-' {
828 out.write_char('-')?;
829 } else if c == sym.decimal_sep {
830 out.write_char('.')?;
831 } else if c == sym.exponent_lower_sym || c == sym.exponent_upper_sym {
832 out.write_char('e')?;
833 }
834 }
835 Ok(())
836 }
837
838 #[allow(clippy::if_same_then_else)]
840 pub fn unmap_num<W: FmtWrite>(
841 formatted: &str,
842 format: &NumberFormat,
843 sym: &NumberSymbols,
844 out: &mut W,
845 ) -> Result<(), NumberFmtError> {
846 let mut buf_sign = String::new();
847 let mut buf_int = String::new();
848 let mut buf_frac = String::new();
849 let mut buf_exp_sign = String::new();
850 let mut buf_exp = String::new();
851
852 let mut it = format.tok.iter();
853 let mut jt = formatted.chars();
854 loop {
855 let Some(t) = it.next() else {
856 break;
857 };
858 let Some(c) = jt.next() else {
859 break;
860 };
861
862 match t {
863 Token::SignInt => {
864 if c == sym.negative_sym {
865 buf_sign.push('-');
866 } else if c == sym.positive_sym {
867 } else {
869 return Err(NumberFmtError::ParseInvalidSign);
870 }
871 }
872 Token::PlusInt => {
873 if c == '-' {
874 buf_sign.push('-');
875 } else if c == '+' {
876 buf_sign.push('+');
877 } else {
878 return Err(NumberFmtError::ParseInvalidSign);
879 }
880 }
881 Token::Digit0(Mode::Integer, _) => {
882 if c.is_ascii_digit() {
883 buf_int.push(c);
884 } else {
885 return Err(NumberFmtError::ParseInvalidDigit);
886 }
887 }
888 Token::Digit(Mode::Integer, _) => {
889 if c.is_ascii_digit() {
890 buf_int.push(c);
891 } else if c == ' ' {
892 } else {
894 return Err(NumberFmtError::ParseInvalidDigit);
895 }
896 }
897 Token::Numeric(Mode::Integer, _, _) => {
898 if c.is_ascii_digit() {
899 buf_int.push(c);
900 } else if c == sym.negative_sym {
901 buf_sign.push('-');
902 } else if c == sym.positive_sym || c == ' ' {
903 } else {
905 return Err(NumberFmtError::ParseInvalidDigit);
906 }
907 }
908 Token::GroupingSep(_, _) => {
909 if let Some(decimal_grp) = sym.decimal_grp {
910 if c == decimal_grp {
911 } else if c == sym.negative_sym {
913 buf_sign.push('-');
914 } else if c == sym.positive_sym || c == ' ' {
915 } else {
917 return Err(NumberFmtError::ParseInvalidGroupingSep);
918 }
919 }
920 }
921 Token::DecimalSep => {
922 if c == sym.decimal_sep {
923 buf_frac.push('.');
924 } else if c == ' ' {
925 } else {
927 return Err(NumberFmtError::ParseInvalidDecimalSep);
928 }
929 }
930 Token::DecimalSepAlways => {
931 if c == sym.decimal_sep {
932 buf_frac.push('.');
933 } else {
934 return Err(NumberFmtError::ParseInvalidDecimalSep);
935 }
936 }
937 Token::Digit0(Mode::Fraction, _) => {
938 if c.is_ascii_digit() {
939 buf_frac.push(c);
940 } else {
941 return Err(NumberFmtError::ParseInvalidDigit);
942 }
943 }
944 Token::Digit(Mode::Fraction, _) => {
945 if c.is_ascii_digit() {
946 buf_frac.push(c);
947 } else if c == ' ' {
948 } else {
950 return Err(NumberFmtError::ParseInvalidDigit);
951 }
952 }
953 Token::Numeric(Mode::Fraction, _, _) => {
954 if c.is_ascii_digit() {
955 buf_frac.push(c);
956 } else if c == ' ' {
957 } else {
959 return Err(NumberFmtError::ParseInvalidDigit);
960 }
961 }
962 Token::ExponentUpper => {
963 if c == sym.exponent_upper_sym {
964 } else if c == ' ' {
966 } else {
968 return Err(NumberFmtError::ParseInvalidExp);
969 }
970 }
971 Token::ExponentLower => {
972 if c == sym.exponent_lower_sym {
973 } else if c == ' ' {
975 } else {
977 return Err(NumberFmtError::ParseInvalidExp);
978 }
979 }
980 Token::Digit0(Mode::Exponent, _) => {
981 if c.is_ascii_digit() {
982 buf_exp.push(c);
983 } else {
984 return Err(NumberFmtError::ParseInvalidDigit);
985 }
986 }
987 Token::Digit(Mode::Exponent, _) => {
988 if c.is_ascii_digit() {
989 buf_exp.push(c);
990 } else if c == ' ' {
991 } else {
993 return Err(NumberFmtError::ParseInvalidDigit);
994 }
995 }
996 Token::Numeric(Mode::Exponent, _, _) => {
997 if c.is_ascii_digit() {
998 buf_exp.push(c);
999 } else if c == sym.negative_sym {
1000 buf_exp_sign.push('-');
1001 } else if c == sym.positive_sym || c == ' ' {
1002 } else {
1004 return Err(NumberFmtError::ParseInvalidDigit);
1005 }
1006 }
1007 Token::SignExp => {
1008 if c == sym.negative_sym {
1009 buf_exp_sign.push('-');
1010 } else if c == sym.positive_sym || c == '+' {
1011 } else {
1013 return Err(NumberFmtError::ParseInvalidExpSign);
1014 }
1015 }
1016 Token::PlusExp => {
1017 if c == '-' {
1018 buf_exp_sign.push('-');
1019 } else if c == '+' {
1020 } else {
1022 return Err(NumberFmtError::ParseInvalidExpSign);
1023 }
1024 }
1025
1026 Token::Currency => {
1027 let mut kt = sym.currency_sym.as_str().chars();
1028 let s = kt.next();
1029 if Some(c) != s {
1030 return Err(NumberFmtError::ParseInvalidCurrency);
1031 }
1032
1033 loop {
1034 match kt.next() {
1035 None => {
1036 break;
1037 }
1038 Some(s) => {
1039 let Some(c) = jt.next() else {
1040 return Err(NumberFmtError::ParseInvalidCurrency);
1041 };
1042 if c != s {
1043 return Err(NumberFmtError::ParseInvalidCurrency);
1044 }
1045 }
1046 }
1047 }
1048 }
1049
1050 Token::Separator(sep) => {
1051 if c == *sep {
1052 } else {
1054 return Err(NumberFmtError::ParseInvalidSeparator);
1055 }
1056 }
1057 }
1058 }
1059
1060 out.write_str(buf_sign.as_str())?;
1061 out.write_str(buf_int.as_str())?;
1062 out.write_str(buf_frac.as_str())?;
1063 if !buf_exp.is_empty() {
1064 out.write_char('e')?;
1065 }
1066 out.write_str(buf_exp_sign.as_str())?;
1067 out.write_str(buf_exp.as_str())?;
1068
1069 Ok(())
1070 }
1071
1072 #[inline]
1077 pub fn map_num<W: FmtWrite, const EXP: bool>(
1078 raw: &str,
1079 format: &NumberFormat,
1080 sym: &NumberSymbols,
1081 out: &mut W,
1082 ) -> Result<(), NumberFmtError> {
1083 let (raw_sign, raw_int, raw_frac, raw_exp_sign, raw_exp) = split_num(raw);
1084
1085 let skip_group = sym.decimal_grp.is_none();
1089 let disp_decimal_grp = sym.decimal_grp.unwrap_or(' ');
1090
1091 let disp_sign = if raw_sign.is_empty() {
1093 sym.positive_sym
1094 } else {
1095 sym.negative_sym
1096 };
1097
1098 let int = raw_int.as_bytes();
1100 let len_int = int.len() as u32;
1101 if len_int > format.len_int {
1102 return Err(NumberFmtError::FmtLenInt);
1103 }
1104
1105 let disp_decimal_sep = if !raw_frac.is_empty() || format.has_frac_0 {
1107 sym.decimal_sep
1108 } else {
1109 ' '
1110 };
1111
1112 let frac = raw_frac.as_bytes();
1114 let len_frac = frac.len() as u32;
1115
1116 let len_exp_sign = raw_exp_sign.len() as u32;
1118
1119 let exp = raw_exp.as_bytes();
1121 let len_exp = exp.len() as u32;
1122
1123 let (disp_exp_upper, disp_exp_lower, disp_exp_sign, shift_exp_n, shift_exp_pos) = if EXP {
1124 let disp_exp_upper = if !raw_exp.is_empty() || format.has_exp_0 {
1125 sym.exponent_upper_sym
1126 } else {
1127 ' '
1128 };
1129 let disp_exp_lower = if !raw_exp.is_empty() || format.has_exp_0 {
1130 sym.exponent_lower_sym
1131 } else {
1132 ' '
1133 };
1134 let disp_exp_sign = if raw_exp_sign.is_empty() {
1135 sym.positive_sym
1136 } else {
1137 sym.negative_sym
1138 };
1139
1140 if len_exp > format.len_exp {
1141 return Err(NumberFmtError::FmtLenExp);
1142 }
1143 if max(len_exp, format.min_exp_sign) + len_exp_sign > format.len_exp {
1145 return Err(NumberFmtError::FmtLenExp);
1146 }
1147 let shift_exp_n = format.len_exp - max(len_exp, format.min_exp_sign) - len_exp_sign;
1149 let shift_exp_pos = max(len_exp, format.min_exp_sign) + len_exp_sign;
1150
1151 (
1152 disp_exp_upper,
1153 disp_exp_lower,
1154 disp_exp_sign,
1155 shift_exp_n,
1156 shift_exp_pos,
1157 )
1158 } else {
1159 (' ', ' ', ' ', 0, 0)
1160 };
1161
1162 let mut used_sign = false;
1163 let mut used_exp_sign = false;
1164
1165 for m in format.tok.iter() {
1166 match m {
1167 Token::SignInt => {
1168 debug_assert!(!used_sign);
1169 out.write_char(disp_sign)?;
1170 used_sign = true;
1171 }
1172 Token::PlusInt => {
1173 debug_assert!(!used_sign);
1174 if raw_sign.is_empty() {
1175 out.write_char('+')?;
1176 } else {
1177 out.write_char('-')?;
1178 }
1179 }
1180 Token::GroupingSep(i, can_be_sign) => {
1181 if skip_group {
1182 } else if len_int > *i {
1184 out.write_char(disp_decimal_grp)?;
1185 } else if *can_be_sign && max(len_int, format.min_int_sign) == *i {
1186 debug_assert!(!used_sign);
1187 out.write_char(disp_sign)?;
1188 used_sign = true;
1189 } else {
1190 out.write_char(' ')?;
1191 }
1192 }
1193 Token::Digit0(Mode::Integer, i) => {
1194 if len_int > *i {
1195 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1196 } else {
1197 out.write_char('0')?;
1198 }
1199 }
1200 Token::Digit(Mode::Integer, i) => {
1201 if len_int > *i {
1202 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1203 } else {
1204 out.write_char(' ')?;
1205 }
1206 }
1207 Token::Numeric(Mode::Integer, i, can_be_sign) => {
1208 if len_int > *i {
1209 out.write_char(int[(len_int - i - 1) as usize] as char)?;
1210 } else if *can_be_sign && max(len_int, format.min_int_sign) == *i {
1211 debug_assert!(!used_sign);
1212 out.write_char(disp_sign)?;
1213 used_sign = true;
1214 } else {
1215 out.write_char(' ')?;
1216 }
1217 }
1218 Token::DecimalSep => {
1219 out.write_char(disp_decimal_sep)?;
1220 }
1221 Token::DecimalSepAlways => {
1222 out.write_char(sym.decimal_sep)?;
1223 }
1224 Token::Digit0(Mode::Fraction, i) => {
1225 if len_frac > *i {
1226 out.write_char(frac[*i as usize] as char)?;
1227 } else {
1228 out.write_char('0')?;
1229 }
1230 }
1231 Token::Digit(Mode::Fraction, i) => {
1232 if len_frac > *i {
1233 out.write_char(frac[*i as usize] as char)?;
1234 } else {
1235 out.write_char(' ')?;
1236 }
1237 }
1238 Token::Numeric(Mode::Fraction, i, _) => {
1239 if len_frac > *i {
1240 out.write_char(frac[*i as usize] as char)?;
1241 } else {
1242 out.write_char(' ')?;
1243 }
1244 }
1245 Token::ExponentUpper => {
1246 if EXP {
1247 out.write_char(disp_exp_upper)?;
1248 }
1249 }
1250 Token::ExponentLower => {
1251 if EXP {
1252 out.write_char(disp_exp_lower)?;
1253 }
1254 }
1255 Token::SignExp => {
1256 if EXP {
1257 debug_assert!(!used_exp_sign);
1258 if raw_exp_sign.is_empty() && sym.positive_sym == ' ' {
1259 out.write_char('+')?;
1261 } else {
1262 out.write_char(disp_exp_sign)?;
1263 }
1264 used_exp_sign = true;
1265 }
1266 }
1267 Token::PlusExp => {
1268 if EXP {
1269 debug_assert!(!used_exp_sign);
1270 if raw_exp_sign.is_empty() {
1271 out.write_char('+')?;
1272 } else {
1273 out.write_char('-')?;
1274 }
1275 used_exp_sign = true;
1276 }
1277 }
1278 Token::Digit0(Mode::Exponent, i) => {
1279 if EXP {
1280 if *i >= shift_exp_pos {
1281 } else if len_exp > *i {
1283 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1284 } else {
1285 out.write_char('0')?;
1286 }
1287 if *i == 0 {
1289 for _ in 0..shift_exp_n {
1290 out.write_char(' ')?;
1291 }
1292 }
1293 }
1294 }
1295 Token::Digit(Mode::Exponent, i) => {
1296 if EXP {
1297 if *i >= shift_exp_pos {
1298 } else if len_exp > *i {
1300 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1301 } else {
1302 out.write_char(' ')?;
1303 }
1304 if *i == 0 {
1306 for _ in 0..shift_exp_n {
1307 out.write_char(' ')?;
1308 }
1309 }
1310 }
1311 }
1312 Token::Numeric(Mode::Exponent, i, can_be_sign) => {
1313 if EXP {
1314 if *i >= shift_exp_pos {
1315 } else if len_exp > *i {
1317 out.write_char(exp[(len_exp - i - 1) as usize] as char)?;
1318 } else if *can_be_sign && max(len_exp, format.min_exp_sign) == *i {
1319 debug_assert!(!used_exp_sign);
1320 out.write_char(disp_exp_sign)?;
1321 used_exp_sign = true;
1322 } else {
1323 out.write_char(' ')?;
1324 }
1325
1326 if *i == 0 {
1328 for _ in 0..shift_exp_n {
1329 out.write_char(' ')?;
1330 }
1331 }
1332 }
1333 }
1334 Token::Currency => {
1335 out.write_str(sym.currency_sym.as_str())?;
1336 }
1337 Token::Separator(v) => {
1338 out.write_char(*v)?;
1339 }
1340 }
1341 }
1342
1343 if !used_sign && !raw_sign.is_empty() {
1344 return Err(NumberFmtError::FmtNoSign);
1345 }
1346 if !used_exp_sign && !raw_exp_sign.is_empty() {
1347 return Err(NumberFmtError::FmtNoExpSign);
1348 }
1349
1350 Ok(())
1351 }
1352
1353 pub fn format_to<W: FmtWrite, Number: LowerExp + Display>(
1355 number: Number,
1356 format: &NumberFormat,
1357 sym: &NumberSymbols,
1358 out: &mut W,
1359 ) -> Result<(), NumberFmtError> {
1360 thread_local! {
1361 static RAW: Cell<String> = const {Cell::new(String::new())};
1362 }
1363
1364 let mut raw = RAW.take();
1365
1366 raw.clear();
1367 let res = if format.has_exp {
1368 write!(raw, "{:.*e}", format.len_frac as usize, number)
1369 .map_err(|_| NumberFmtError::Fmt)?;
1370 map_num::<_, true>(raw.as_str(), format, sym, out)
1371 } else {
1372 write!(raw, "{:.*}", format.len_frac as usize, number)
1373 .map_err(|_| NumberFmtError::Fmt)?;
1374 map_num::<_, false>(raw.as_str(), format, sym, out)
1375 };
1376
1377 match res {
1378 Ok(v) => {
1379 RAW.set(raw);
1380 Ok(v)
1381 }
1382 Err(e) => {
1383 RAW.set(raw);
1384 Err(e)
1385 }
1386 }
1387 }
1388
1389 pub fn parse_fmt<F: FromStr>(
1391 s: &str,
1392 fmt: &NumberFormat,
1393 sym: &NumberSymbols,
1394 ) -> Result<F, NumberFmtError> {
1395 thread_local! {
1396 static RAW: Cell<String> = const {Cell::new(String::new())};
1397 }
1398
1399 let mut raw = RAW.take();
1400
1401 raw.clear();
1402 unmap_num(s, fmt, sym, &mut raw)?;
1403
1404 match raw.parse::<F>() {
1405 Ok(v) => {
1406 RAW.set(raw);
1407 Ok(v)
1408 }
1409 Err(_) => {
1410 RAW.set(raw);
1411 Err(NumberFmtError::Parse)
1412 }
1413 }
1414 }
1415
1416 pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1419 thread_local! {
1420 static RAW: Cell<String> = const {Cell::new(String::new())};
1421 }
1422
1423 let mut raw = RAW.take();
1424
1425 raw.clear();
1426 clean_num(s, sym, &mut raw)?;
1427
1428 match raw.parse::<F>() {
1429 Ok(v) => {
1430 RAW.set(raw);
1431 Ok(v)
1432 }
1433 Err(_) => {
1434 RAW.set(raw);
1435 Err(NumberFmtError::Parse)
1436 }
1437 }
1438 }
1439}
1440
1441pub fn format<Number: LowerExp + Display>(
1444 number: Number,
1445 pattern: &str,
1446) -> Result<String, NumberFmtError> {
1447 let fmt = NumberFormat::new(pattern)?;
1448 let mut out = String::new();
1449 core::format_to(number, &fmt, fmt.sym(), &mut out)?;
1450 Ok(out)
1451}
1452
1453pub fn format_to<W: FmtWrite, Number: LowerExp + Display>(
1456 number: Number,
1457 pattern: &str,
1458 out: &mut W,
1459) -> Result<(), NumberFmtError> {
1460 let fmt = NumberFormat::new(pattern)?;
1461 core::format_to(number, &fmt, fmt.sym(), out)
1462}
1463
1464pub fn formats<Number: LowerExp + Display>(
1466 number: Number,
1467 pattern: &str,
1468 sym: &NumberSymbols,
1469) -> Result<String, NumberFmtError> {
1470 let format = NumberFormat::new(pattern)?;
1471 let mut out = String::new();
1472 core::format_to(number, &format, sym, &mut out)?;
1473 Ok(out)
1474}
1475
1476pub fn formats_to<W: FmtWrite, Number: LowerExp + Display>(
1478 number: Number,
1479 pattern: &str,
1480 sym: &NumberSymbols,
1481 out: &mut W,
1482) -> Result<(), NumberFmtError> {
1483 let format = NumberFormat::new(pattern)?;
1484 core::format_to(number, &format, sym, out)
1485}
1486
1487pub fn fmt<Number: LowerExp + Display>(number: Number, format: &NumberFormat) -> String {
1489 let mut out = String::new();
1490 _ = core::format_to(number, format, &format.sym, &mut out);
1491 out
1492}
1493
1494pub fn fmt_to<W: FmtWrite, Number: LowerExp + Display>(
1496 number: Number,
1497 format: &NumberFormat,
1498 out: &mut W,
1499) {
1500 _ = core::format_to(number, format, &format.sym, out)
1501}
1502
1503pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1506 core::parse_sym(s, sym)
1507}
1508
1509pub fn parse_fmt<F: FromStr>(s: &str, fmt: &NumberFormat) -> Result<F, NumberFmtError> {
1512 core::parse_fmt(s, fmt, &fmt.sym)
1513}
1514
1515pub fn parse_format<F: FromStr>(
1518 s: &str,
1519 pattern: &str,
1520 sym: &NumberSymbols,
1521) -> Result<F, NumberFmtError> {
1522 let format = NumberFormat::new(pattern)?;
1523 core::parse_fmt(s, &format, sym)
1524}