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