format_num_pattern/
lib.rs

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/// Symbols for number formatting.
13#[derive(Debug, PartialEq, Eq, Clone, Copy)]
14pub struct NumberSymbols {
15    /// Decimal separator
16    pub decimal_sep: char,
17    /// Decimal grouping
18    pub decimal_grp: Option<char>,
19    /// Minus sign
20    pub negative_sym: char,
21    /// Plus sign
22    pub positive_sym: char,
23    /// Exponent
24    pub exponent_upper_sym: char,
25    /// Exponent
26    pub exponent_lower_sym: char,
27    /// Currency
28    pub currency_sym: CurrencySym,
29    // todo: zero-digit, infinity, nan
30}
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    /// Uses the locale information provided by `pure_rust_locales`.
52    ///
53    /// This function sets
54    /// * decimal_sep to LC_NUMERIC::DECIMAL_POINT,
55    /// * decimal_grp to LC_NUMERIC::THOUSANDS_SEP
56    ///
57    /// Fills the rest with defaults.
58    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    /// Uses the locale information provided by `pure_rust_locales`.
71    ///
72    /// This function sets
73    /// * decimal_sep to LC_MONETARY::MON_DECIMAL_POINT,
74    /// * decimal_grp to LC_MONETARY::MON_THOUSANDS_SEP
75    /// * negative_sym to LC_MONETARY::NEGATIVE_SIGN
76    /// * positive_sym to LC_MONETARY::POSITIVE_SIGN
77    /// * currency_sym to LC_MONETARY::CURRENCY_SYMBOL
78    ///
79    /// Fills the rest with defaults.
80    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    /// Uses the locale information provided by `pure_rust_locales`.
93    ///
94    /// This function sets
95    /// * decimal_sep to LC_MONETARY::MON_DECIMAL_POINT,
96    /// * decimal_grp to LC_MONETARY::MON_THOUSANDS_SEP
97    /// * negative_sym to LC_MONETARY::NEGATIVE_SIGN
98    /// * positive_sym to LC_MONETARY::POSITIVE_SIGN
99    /// * currency_sym to LC_MONETARY::INT_CURR_SYMBOL
100    ///
101    /// Fills the rest with defaults.
102    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// first char or default
116#[inline]
117fn first_or(s: &str, default: char) -> char {
118    s.chars().next().unwrap_or(default)
119}
120
121// first char or default
122#[inline]
123fn first_opt(s: &str) -> Option<char> {
124    s.chars().next()
125}
126
127/// Currency symbol.
128/// Const constructable short inline string.
129#[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    /// New currency symbol.
146    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    /// Convert back to &str
165    pub fn as_str(&self) -> &str {
166        // Safety:
167        // Copied from &str and never modified.
168        unsafe { from_utf8_unchecked(&self.sym[..self.len as usize]) }
169    }
170
171    /// Symbol len.
172    pub const fn len(&self) -> usize {
173        self.len as usize
174    }
175
176    /// Symbol empty.
177    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/// Number mode.
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196enum Mode {
197    Integer,
198    Fraction,
199    Exponent,
200}
201
202/// Tokens for the format.
203///
204/// Digit0, Digit, Numeric, NumericOpt, GroupingSep hold an digit-index.
205/// Depending on mode that's the index into the integer, fraction or exponent part of
206/// the number.
207///
208/// Numeric has an extra flag, to mark if a sign at this position is possible.
209/// Next to a grouping separator there can be no sign, it will be at the position
210/// of the grouping separator.
211#[allow(variant_size_differences)]
212#[derive(Debug, Clone, Copy, PartialEq, Eq)]
213enum Token {
214    /// Mask char "0". Digit or 0
215    Digit0(Mode, u32),
216    /// Mask char "9". Digit or space
217    Digit(Mode, u32),
218    /// Mask char "#". Digit or sign or space
219    Numeric(Mode, u32, bool),
220    /// Mask char "-". Integer sign.
221    SignInt,
222    /// Mask char "+". Integer sign.
223    PlusInt,
224    /// Mask char ".". Decimal separator.
225    DecimalSep,
226    /// Mask char ":". Decimal separator, always displayed.
227    DecimalSepAlways,
228    /// Mask char ",". Grouping separator.
229    GroupingSep(u32, bool),
230    /// Mask char "E". Exponent separator.
231    ExponentUpper,
232    /// Mask char "e". Exponent separator.
233    ExponentLower,
234    /// Mask char "-". Exponent sign.
235    SignExp,
236    /// Mask char "+". Exponent sign.
237    PlusExp,
238    /// Mask char "$". Currency. Variable length.
239    Currency,
240    /// Other separator char to output literally. May be escaped with '\\'.
241    Separator(char),
242}
243
244/// Holds the pattern for the number format and some additional data.
245#[derive(Debug, Default, Clone, PartialEq, Eq)]
246pub struct NumberFormat {
247    /// Minimum position where a sign can be placed. Just left of a `Token::Digit0`
248    min_int_sign: u32,
249    /// Number of integer digits.
250    len_int: u32,
251
252    /// Decides which std-format is used. If true it's `{:e}` otherwise plain `{}`
253    has_exp: bool,
254    /// Has an exponent with a '0' pattern.
255    has_exp_0: bool,
256    /// Minimum position where a sign can be placed. Just left of a `Token::Digit0`
257    min_exp_sign: u32,
258    /// Number of exponent digits
259    len_exp: u32,
260
261    /// Has a fraction with a '0' pattern.
262    has_frac_0: bool,
263    /// The required precision for this format. Is used for the underlying std-format.
264    len_frac: u8,
265
266    /// Tokens.
267    tok: Vec<Token>,
268    /// Symbols.
269    sym: NumberSymbols,
270}
271
272/// Errors
273#[derive(Debug, PartialEq, Eq, Clone, Copy)]
274pub enum NumberFmtError {
275    /// General formatting error. Mostly from `write!()`
276    Fmt,
277    /// Integer len of the source is too long.
278    FmtLenInt,
279    /// Exponent len of the source is too long.
280    FmtLenExp,
281    /// Number is negative, but there is no place to show.
282    FmtNoSign,
283    /// Exponent is negative, but there is no place to show.
284    FmtNoExpSign,
285    /// General parse error. Mostly from `FromStr::parse()`
286    Parse,
287    /// Misplaced decimal separator in the pattern. Invalid decimal separator when parsing.
288    ParseInvalidDecimalSep,
289    /// Invalid sign in the pattern. Invalid sign when parsing.
290    ParseInvalidSign,
291    /// Invalid exponent in the pattern. Invalid exponent when parsing.
292    ParseInvalidExp,
293    /// Invalid exp sign in the pattern. Invalid exp sign when parsing.
294    ParseInvalidExpSign,
295    /// Unescaped char in the pattern.
296    ParseUnescaped,
297    /// Invalid digit when parsing.
298    ParseInvalidDigit,
299    /// Invalid grp sep when parsing.
300    ParseInvalidGroupingSep,
301    /// Invalid currency symbol when parsing.
302    ParseInvalidCurrency,
303    /// Invalid separator when parsing.
304    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    /// New format from pattern.
352    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    /// New format from pattern + symbols
358    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    /// New format from token-array.
364    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    /// Parses the format string. Uses the default symbol table.
509    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    /// Symbols
588    pub fn sym(&self) -> &NumberSymbols {
589        &self.sym
590    }
591
592    /// Formats and unwraps any error.
593    /// The error is written to the result string using {:?}.
594    /// So this one may be convenient in some situations, but ...
595    #[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    /// Formats.
609    #[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    /// Formats to a buffer.
620    #[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    /// Parse using the exact format.
630    /// See [ParseNumber::parse_sym()](crate::number::ParseNumber::parse_sym()]
631    #[inline]
632    pub fn parse<F: FromStr>(&self, s: &str) -> Result<F, NumberFmtError> {
633        core::parse_fmt(s, self, &self.sym)
634    }
635}
636
637/// Parses a number from a &str.
638pub trait ParseNumber {
639    /// Parse the number after applying [core::clean_num()].
640    /// This removes everything but digits, decimal sym and sign and then parses.
641    /// Uses the given symbols for the translation.
642    fn parse_sym<F: FromStr>(&self, sym: &NumberSymbols) -> Result<F, NumberFmtError>;
643    /// Parse the number after applying [core::unmap_num()]
644    /// Creates a raw number by unapplying the exact pattern.
645    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
658/// Format a number according to a format string.
659pub trait DisplayNumber
660where
661    Self: Copy + LowerExp + Display,
662{
663    /// Format using the format-string. Uses the given symbols.
664    fn format<'a>(
665        &self,
666        pattern: &'a str,
667        sym: &'a NumberSymbols,
668    ) -> Result<FormattedNumber<'a, Self>, NumberFmtError>;
669
670    /// Format using the [NumberFormat]
671    fn fmt<'a>(&self, format: &'a NumberFormat) -> RefFormattedNumber<'a, Self>;
672}
673
674/// Holds a temporary result from [DisplayNumber]. The only purpose is as anchor for the
675/// Display trait.
676#[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/// Holds a temporary result from [DisplayNumber]. The only purpose is as anchor for the
695/// Display trait.
696#[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        // everything is ascii
766        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    /// Get the clean number.
816    ///
817    /// Takes only digits and maps backwards according to the symbol table.
818    /// This will only work if you don't use separators that can be mistaken
819    /// with one of those symbols.
820    ///
821    /// Removes any leading zeros too.
822    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                // noop
838            } else if c == '+' {
839                // todo: ???
840            } 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    /// Unmap the formatted string back to a format that `f64::parse()` can understand.
850    #[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                        // ok
879                    } 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                        // ok
904                    } 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                        // ok
915                    } 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                            // ok
923                        } else if c == sym.negative_sym {
924                            buf_sign.push('-');
925                        } else if c == sym.positive_sym || c == ' ' {
926                            // ok
927                        } 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                        // ok
937                    } 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                        // ok
960                    } 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                        // ok
969                    } else {
970                        return Err(NumberFmtError::ParseInvalidDigit);
971                    }
972                }
973                Token::ExponentUpper => {
974                    if c == sym.exponent_upper_sym {
975                        // ok
976                    } else if c == ' ' {
977                        // ok
978                    } else {
979                        return Err(NumberFmtError::ParseInvalidExp);
980                    }
981                }
982                Token::ExponentLower => {
983                    if c == sym.exponent_lower_sym {
984                        // ok
985                    } else if c == ' ' {
986                        // ok
987                    } 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                        // ok
1003                    } 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                        // ok
1014                    } 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                        // ok
1023                    } else {
1024                        return Err(NumberFmtError::ParseInvalidExpSign);
1025                    }
1026                }
1027                Token::PlusExp => {
1028                    if c == '-' {
1029                        buf_exp_sign.push('-');
1030                    } else if c == '+' {
1031                        // ok
1032                    } 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                        // ok
1064                    } 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    /// Takes a raw number string and applies the format.
1084    ///
1085    /// The raw number should be in a format produced by the format! macro. decimal point is '.',
1086    /// exponent is 'e' and negative sign is '-'.
1087    #[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        // locale mapping
1097
1098        // grouping
1099        let skip_group = sym.decimal_grp.is_none();
1100        let disp_decimal_grp = sym.decimal_grp.unwrap_or(' ');
1101
1102        // sign
1103        let disp_sign = if raw_sign.is_empty() {
1104            sym.positive_sym
1105        } else {
1106            sym.negative_sym
1107        };
1108
1109        // integer
1110        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        // dec-sep
1117        let disp_decimal_sep = if !raw_frac.is_empty() || format.has_frac_0 {
1118            sym.decimal_sep
1119        } else {
1120            ' '
1121        };
1122
1123        // fraction
1124        let frac = raw_frac.as_bytes();
1125        let len_frac = frac.len() as u32;
1126
1127        // exponent sign
1128        let len_exp_sign = raw_exp_sign.len() as u32;
1129
1130        // exponent
1131        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            // not enough space for the exponent
1155            if max(len_exp, format.min_exp_sign) + len_exp_sign > format.len_exp {
1156                return Err(NumberFmtError::FmtLenExp);
1157            }
1158            // left shift the exponent and fill the rest with ' '.
1159            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                        // noop
1194                    } 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                            // explicit sign in the exponent shows '+'.
1271                            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                            // left-shift exponent
1293                        } 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                        // append shifted digits as blank
1299                        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                            // left-shift exponent
1310                        } 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                        // append shifted digits as blank
1316                        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                            // left-shift exponent
1327                        } 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                        // append shifted digits as blank
1338                        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    /// Formats the number and writes the result to out.
1365    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    /// Parse the number according to the exact format.
1401    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    /// Parse the number only using the symbols for translation.
1428    /// Takes digits and some specials and ignores the rest.
1429    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
1452/// Format a Number according to the format string.
1453/// Uses the default symbols.
1454pub 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
1464/// Format a Number according to the format string.
1465/// Uses the default symbols.
1466pub 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
1475/// Format a Number according to the format string.
1476pub 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
1487/// Format a Number according to the format string.
1488pub 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
1498/// Format a Number according to the format.
1499pub 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
1505/// Format a Number according to the format.
1506pub 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
1514/// Parse using the NumberSymbols.
1515/// Parses the number after applying [core::clean_num]
1516pub fn parse_sym<F: FromStr>(s: &str, sym: &NumberSymbols) -> Result<F, NumberFmtError> {
1517    core::parse_sym(s, sym)
1518}
1519
1520/// Parse using the NumberFormat.
1521/// Parses the number after applying [core::unmap_num]
1522pub fn parse_fmt<F: FromStr>(s: &str, fmt: &NumberFormat) -> Result<F, NumberFmtError> {
1523    core::parse_fmt(s, fmt, &fmt.sym)
1524}
1525
1526/// Parse using the NumberFormat.
1527/// Parses the number after applying [core::unmap_num]
1528pub 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}