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