1mod enums;
14pub use enums::*;
15use serde::{Deserialize, Serialize};
16use serde_with::skip_serializing_none;
17use strum::{Display, EnumIter};
18use ts_rs::TS;
19
20#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, TS)]
21#[serde(rename_all = "camelCase", tag = "style")]
22pub enum NumberFormatStyle {
23 #[default]
24 Decimal,
25 Currency(CurrencyNumberFormatStyle),
26 Percent,
27 Unit(UnitNumberFormatStyle),
28}
29
30#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
31#[serde(rename_all = "camelCase")]
32pub enum CurrencyDisplay {
33 Code,
34 #[default]
35 Symbol,
36 NarrowSymbol,
37 Name,
38}
39
40#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
41#[serde(rename_all = "camelCase")]
42pub enum CurrencySign {
43 #[default]
44 Standard,
45 Accounting,
46}
47
48#[skip_serializing_none]
49#[derive(Default, Serialize, Deserialize, Debug, PartialEq, Clone, TS)]
50#[serde(rename_all = "camelCase")]
51pub struct CurrencyNumberFormatStyle {
52 #[serde(default)]
53 pub currency: CurrencyCode,
54 pub currency_display: Option<CurrencyDisplay>,
55 pub currency_sign: Option<CurrencySign>,
56}
57
58#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
59#[serde(rename_all = "camelCase")]
60pub enum UnitDisplay {
61 #[default]
62 Short,
63 Narrow,
64 Long,
65}
66
67#[skip_serializing_none]
68#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, TS)]
69#[serde(rename_all = "camelCase")]
70pub struct UnitNumberFormatStyle {
71 #[serde(default)]
72 pub unit: Unit,
73 pub unit_display: Option<UnitDisplay>,
74}
75
76#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
77#[serde(rename_all = "camelCase")]
78pub enum RoundingPriority {
79 #[default]
80 Auto,
81 MorePrecision,
82 LessPrecision,
83}
84
85#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
86#[serde(rename_all = "camelCase")]
87pub enum RoundingMode {
88 Ceil,
89 Floor,
90 Expand,
91 Trunc,
92 HalfCeil,
93 HalfFloor,
94 #[default]
95 HalfExpand,
96 HalfTrunc,
97 HalfEven,
98}
99
100#[derive(Default, Debug, PartialEq, Clone, TS)]
101pub enum RoundingIncrement {
102 #[default]
103 Auto,
104 Custom(f64),
105}
106impl std::fmt::Display for RoundingIncrement {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 match self {
109 RoundingIncrement::Auto => f.write_str("Auto"),
110 RoundingIncrement::Custom(val) => f.write_fmt(format_args!("{val}")),
111 }
112 }
113}
114
115pub const ROUNDING_INCREMENTS: [f64; 15] = [
116 1., 2., 5., 10., 20., 25., 50., 100., 200., 250., 500., 1000., 2000., 2500., 5000.,
117];
118
119#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
120#[serde(rename_all = "camelCase")]
121pub enum TrailingZeroDisplay {
122 #[default]
123 Auto,
124 StripIfInteger,
125}
126
127#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, TS)]
128#[serde(rename_all = "camelCase", tag = "notation")]
129pub enum Notation {
130 #[default]
131 Standard,
132 Scientific,
133 Engineering,
134 Compact(CompactDisplay),
135}
136
137#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
138#[serde(rename_all = "camelCase", tag = "compactDisplay")]
139pub enum CompactDisplay {
140 #[default]
141 Short,
142 Long,
143}
144
145#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
146#[serde(rename_all = "snake_case")]
147pub enum UseGrouping {
148 Always,
149
150 #[default]
151 Auto,
152 Min2, #[serde(untagged)]
155 False(bool),
156}
157
158#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Clone, Copy, EnumIter, Display, TS)]
159#[serde(rename_all = "camelCase")]
160pub enum SignDisplay {
161 #[default]
162 Auto,
163 Always,
164 ExceptZero,
165 Negative,
166 Never,
167}
168
169#[skip_serializing_none]
170#[derive(Serialize, Deserialize, Debug, Default, PartialEq, Clone, TS)]
171#[serde(rename_all = "camelCase")]
172pub struct CustomNumberFormatConfig {
173 #[serde(flatten)]
174 #[ts(skip)]
175 pub _style: Option<NumberFormatStyle>,
176
177 pub minimum_integer_digits: Option<f64>,
182 pub minimum_fraction_digits: Option<f64>,
183 pub maximum_fraction_digits: Option<f64>,
184 pub minimum_significant_digits: Option<f64>,
185 pub maximum_significant_digits: Option<f64>,
186 pub rounding_priority: Option<RoundingPriority>,
187
188 pub rounding_increment: Option<f64>,
192 pub rounding_mode: Option<RoundingMode>,
193 pub trailing_zero_display: Option<TrailingZeroDisplay>,
194
195 #[serde(flatten)]
196 #[ts(skip)]
197 pub _notation: Option<Notation>,
198 pub use_grouping: Option<UseGrouping>,
199 pub sign_display: Option<SignDisplay>,
200}
201
202impl CustomNumberFormatConfig {
203 pub fn filter_default(self, is_float: bool) -> Self {
204 let (frac_min, frac_max) = if is_float { (2., 2.) } else { (0., 0.) };
205 let rounding_increment = self.rounding_increment;
206 let use_grouping = self
207 .use_grouping
208 .filter(|val| *val != UseGrouping::default());
209
210 let mut minimum_fraction_digits =
211 self.minimum_fraction_digits.filter(|val| *val != frac_min);
212
213 let mut maximum_fraction_digits =
214 self.maximum_fraction_digits.filter(|val| *val != frac_max);
215
216 let mut show_frac = is_float
217 && (minimum_fraction_digits.is_some()
218 || maximum_fraction_digits.is_some()
219 || use_grouping.is_some()
220 || matches!(
221 self._style,
222 Some(NumberFormatStyle::Percent | NumberFormatStyle::Unit(_))
223 ))
224 || !is_float && matches!(self._style, Some(NumberFormatStyle::Currency(_)));
225
226 if rounding_increment.is_some() {
229 show_frac = true;
230 minimum_fraction_digits = Some(0.);
231 maximum_fraction_digits = Some(0.);
232 }
233
234 let minimum_significant_digits = self.minimum_significant_digits.filter(|val| *val != 1.);
235 let maximum_significant_digits = self.maximum_significant_digits.filter(|val| *val != 21.);
236 let show_sig = minimum_significant_digits.is_some() || maximum_significant_digits.is_some();
237 Self {
238 _style: self
239 ._style
240 .filter(|style| !matches!(style, NumberFormatStyle::Decimal)),
241 minimum_integer_digits: self.minimum_integer_digits.filter(|val| *val != 1.),
242 minimum_fraction_digits: show_frac
243 .then_some(minimum_fraction_digits.unwrap_or(frac_min)),
244 maximum_fraction_digits: show_frac
245 .then_some(maximum_fraction_digits.unwrap_or(frac_max)),
246 minimum_significant_digits: show_sig
247 .then_some(minimum_significant_digits.unwrap_or(1.)),
248 maximum_significant_digits: show_sig
249 .then_some(minimum_significant_digits.unwrap_or(21.)),
250 rounding_priority: self
251 .rounding_priority
252 .filter(|val| *val != RoundingPriority::default()),
253 rounding_increment,
254 rounding_mode: self
255 .rounding_mode
256 .filter(|val| *val != RoundingMode::default()),
257 trailing_zero_display: self
258 .trailing_zero_display
259 .filter(|val| *val != TrailingZeroDisplay::default()),
260 _notation: self
261 ._notation
262 .filter(|notation| !matches!(notation, Notation::Standard)),
263 use_grouping,
264 sign_display: self
265 .sign_display
266 .filter(|val| *val != SignDisplay::default()),
267 }
268 }
269}