Skip to main content

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