1use core::fmt::Write;
8
9use crate::grouper;
10use crate::options::*;
11use crate::parts;
12use crate::provider::*;
13use fixed_decimal::Decimal;
14use fixed_decimal::Sign;
15use writeable::Part;
16use writeable::PartsWrite;
17use writeable::Writeable;
18
19#[derive(Debug, PartialEq, Clone)]
22pub struct FormattedDecimal<'l> {
23 pub(crate) value: &'l Decimal,
24 pub(crate) options: &'l DecimalFormatterOptions,
25 pub(crate) symbols: &'l DecimalSymbols<'l>,
26 pub(crate) digits: &'l [char; 10],
27}
28
29impl FormattedDecimal<'_> {
30 fn get_affixes(&self) -> Option<(Part, (&str, &str))> {
32 match self.value.sign() {
33 Sign::None => None,
34 Sign::Negative => Some((parts::MINUS_SIGN, self.symbols.minus_sign_affixes())),
35 Sign::Positive => Some((parts::PLUS_SIGN, self.symbols.plus_sign_affixes())),
36 }
37 }
38}
39
40impl Writeable for FormattedDecimal<'_> {
41 fn write_to_parts<W>(&self, w: &mut W) -> core::result::Result<(), core::fmt::Error>
42 where
43 W: writeable::PartsWrite + ?Sized,
44 {
45 let affixes = self.get_affixes();
46 if let Some((part, affixes)) = affixes {
47 w.with_part(part, |w| w.write_str(affixes.0))?;
48 }
49 let range = self.value.absolute.magnitude_range();
50 let upper_magnitude = *range.end();
51 let mut range = range.rev();
52 let mut has_fraction = false;
53 w.with_part(parts::INTEGER, |w| {
54 loop {
55 let m = match range.next() {
56 Some(m) if m < 0 => {
57 has_fraction = true;
58 break Ok(());
59 }
60 Some(m) => m,
61 None => {
62 break Ok(());
63 }
64 };
65 #[allow(clippy::indexing_slicing)] w.write_char(self.digits[self.value.digit_at(m) as usize])?;
67 if grouper::check(
68 upper_magnitude,
69 m,
70 self.options.grouping_strategy.unwrap_or_default(),
71 self.symbols.grouping_sizes,
72 ) {
73 w.with_part(parts::GROUP, |w| {
74 w.write_str(self.symbols.grouping_separator())
75 })?;
76 }
77 }
78 })?;
79 if has_fraction {
80 w.with_part(parts::DECIMAL, |w| {
81 w.write_str(self.symbols.decimal_separator())
82 })?;
83 w.with_part(parts::FRACTION, |w| {
84 let mut m = -1; loop {
86 #[allow(clippy::indexing_slicing)] w.write_char(self.digits[self.value.digit_at(m) as usize])?;
88 m = match range.next() {
89 Some(m) => m,
90 None => {
91 break Ok(());
92 }
93 };
94 }
95 })?;
96 }
97 if let Some((part, affixes)) = affixes {
98 w.with_part(part, |w| w.write_str(affixes.1))?;
99 }
100 Ok(())
101 }
102}
103
104writeable::impl_display_with_writeable!(FormattedDecimal<'_>);
105
106#[cfg(test)]
107mod tests {
108 use icu_locale_core::locale;
109 use writeable::assert_writeable_eq;
110
111 use crate::DecimalFormatter;
112
113 #[test]
114 pub fn test_es_mx() {
115 let locale = locale!("es-MX").into();
116 let fmt = DecimalFormatter::try_new(locale, Default::default()).unwrap();
117 let fd = "12345.67".parse().unwrap();
118 assert_writeable_eq!(fmt.format(&fd), "12,345.67");
119 }
120}