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