linkrs_core/value/
decimal128.rs1use dec::Decimal128;
25use serde::{Deserialize, Serialize};
26use std::fmt;
27use std::str::FromStr;
28
29#[derive(Debug, Clone)]
44pub struct Decimal128Value {
45 inner: Decimal128,
46}
47
48impl Serialize for Decimal128Value {
49 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
50 where
51 S: serde::Serializer,
52 {
53 self.to_string().serialize(serializer)
54 }
55}
56
57impl<'de> Deserialize<'de> for Decimal128Value {
58 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59 where
60 D: serde::Deserializer<'de>,
61 {
62 let s = String::deserialize(deserializer)?;
63 Self::from_str(&s).map_err(serde::de::Error::custom)
64 }
65}
66
67impl Decimal128Value {
68 pub fn new(inner: Decimal128) -> Self {
70 Self { inner }
71 }
72
73 pub fn from_i64(n: i64) -> Self {
75 Self {
76 inner: Decimal128::from(n),
77 }
78 }
79
80 pub fn from_u64(n: u64) -> Self {
82 Self {
83 inner: Decimal128::from(n),
84 }
85 }
86
87 pub fn from_f64(n: f64) -> Option<Self> {
89 let s = n.to_string();
90 Self::from_str(&s).ok()
91 }
92
93 pub fn inner(&self) -> &Decimal128 {
95 &self.inner
96 }
97
98 pub fn inner_mut(&mut self) -> &mut Decimal128 {
100 &mut self.inner
101 }
102
103 pub fn add(&self, other: &Self) -> Result<Self, String> {
105 Ok(Self {
106 inner: self.inner + other.inner,
107 })
108 }
109
110 pub fn sub(&self, other: &Self) -> Result<Self, String> {
112 Ok(Self {
113 inner: self.inner - other.inner,
114 })
115 }
116
117 pub fn mul(&self, other: &Self) -> Result<Self, String> {
119 Ok(Self {
120 inner: self.inner * other.inner,
121 })
122 }
123
124 pub fn div(&self, other: &Self) -> Result<Self, String> {
126 if other.inner == Decimal128::ZERO {
127 return Err("division error".to_string());
128 }
129 Ok(Self {
130 inner: self.inner / other.inner,
131 })
132 }
133
134 pub fn rem(&self, other: &Self) -> Result<Self, String> {
136 if other.inner == Decimal128::ZERO {
137 return Err("division error".to_string());
138 }
139 Ok(Self {
140 inner: self.inner % other.inner,
141 })
142 }
143
144 pub fn abs(&self) -> Self {
146 Self {
147 inner: if self.inner < Decimal128::ZERO {
148 -self.inner
149 } else {
150 self.inner
151 },
152 }
153 }
154
155 pub fn neg(&self) -> Self {
157 Self { inner: -self.inner }
158 }
159
160 pub fn round_dp(&self, dp: u32) -> Self {
162 let s = self.to_string();
163 if let Some(dot_pos) = s.find('.') {
164 let integer_part = &s[..dot_pos];
165 let fractional_part = &s[dot_pos + 1..];
166 if fractional_part.len() <= dp as usize {
167 return self.clone();
168 }
169 let rounded_fractional = &fractional_part[..dp as usize];
170 let rounded_str = format!("{}.{}", integer_part, rounded_fractional);
171 Self::from_str(&rounded_str).unwrap_or_else(|_| self.clone())
172 } else {
173 self.clone()
174 }
175 }
176
177 pub fn floor(&self) -> Self {
179 let s = self.to_string();
180 if let Some(dot_pos) = s.find('.') {
181 let integer_part = &s[..dot_pos];
182 Self::from_str(integer_part).unwrap_or_else(|_| self.clone())
183 } else {
184 self.clone()
185 }
186 }
187
188 pub fn ceil(&self) -> Self {
190 let s = self.to_string();
191 if let Some(dot_pos) = s.find('.') {
192 let fractional_part = &s[dot_pos + 1..];
193 if fractional_part.chars().all(|c| c == '0') {
194 self.clone()
195 } else {
196 let integer_part = &s[..dot_pos];
197 let int_value: i64 = integer_part.parse().unwrap_or(0);
198 let ceil_value = if self.inner >= Decimal128::ZERO {
199 int_value + 1
200 } else {
201 int_value
202 };
203 Self::from_i64(ceil_value)
204 }
205 } else {
206 self.clone()
207 }
208 }
209
210 pub fn is_zero(&self) -> bool {
212 self.inner == Decimal128::ZERO
213 }
214
215 pub fn is_negative(&self) -> bool {
217 self.inner < Decimal128::ZERO
218 }
219
220 pub fn is_positive(&self) -> bool {
222 self.inner > Decimal128::ZERO
223 }
224
225 pub fn is_nan(&self) -> bool {
227 self.inner == Decimal128::NAN
228 }
229}
230
231impl Default for Decimal128Value {
232 fn default() -> Self {
233 Self {
234 inner: Decimal128::ZERO,
235 }
236 }
237}
238
239impl fmt::Display for Decimal128Value {
240 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241 write!(f, "{}", self.inner)
242 }
243}
244
245impl FromStr for Decimal128Value {
246 type Err = String;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 Decimal128::from_str(s)
250 .map(|inner| Self { inner })
251 .map_err(|e| format!("Decimal128 parse failed: {}", e))
252 }
253}
254
255impl std::ops::Add for &Decimal128Value {
256 type Output = Decimal128Value;
257
258 fn add(self, other: Self) -> Self::Output {
259 Decimal128Value {
260 inner: self.inner + other.inner,
261 }
262 }
263}
264
265impl std::ops::Sub for &Decimal128Value {
266 type Output = Decimal128Value;
267
268 fn sub(self, other: Self) -> Self::Output {
269 Decimal128Value {
270 inner: self.inner - other.inner,
271 }
272 }
273}
274
275impl std::ops::Mul for &Decimal128Value {
276 type Output = Decimal128Value;
277
278 fn mul(self, other: Self) -> Self::Output {
279 Decimal128Value {
280 inner: self.inner * other.inner,
281 }
282 }
283}
284
285impl std::ops::Div for &Decimal128Value {
286 type Output = Decimal128Value;
287
288 fn div(self, other: Self) -> Self::Output {
289 Decimal128Value {
290 inner: self.inner / other.inner,
291 }
292 }
293}
294
295impl std::ops::Rem for &Decimal128Value {
296 type Output = Decimal128Value;
297
298 fn rem(self, other: Self) -> Self::Output {
299 Decimal128Value {
300 inner: self.inner % other.inner,
301 }
302 }
303}
304
305impl std::ops::Neg for Decimal128Value {
306 type Output = Decimal128Value;
307
308 fn neg(self) -> Self::Output {
309 Decimal128Value { inner: -self.inner }
310 }
311}
312
313impl PartialEq for Decimal128Value {
314 fn eq(&self, other: &Self) -> bool {
315 self.inner == other.inner
316 }
317}
318
319impl Eq for Decimal128Value {}
320
321impl PartialOrd for Decimal128Value {
322 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
323 Some(self.cmp(other))
324 }
325}
326
327impl Ord for Decimal128Value {
328 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
329 if self.inner < other.inner {
330 std::cmp::Ordering::Less
331 } else if self.inner > other.inner {
332 std::cmp::Ordering::Greater
333 } else {
334 std::cmp::Ordering::Equal
335 }
336 }
337}
338
339impl std::hash::Hash for Decimal128Value {
340 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
341 self.to_string().hash(state);
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348
349 #[test]
350 fn test_from_str() {
351 let d = "123.456".parse::<Decimal128Value>().expect("parse failed");
352 assert_eq!(d.to_string(), "123.456");
353 }
354
355 #[test]
356 fn test_from_i64() {
357 let d = Decimal128Value::from_i64(123456);
358 assert_eq!(d.to_string(), "123456");
359 }
360
361 #[test]
362 fn test_add() {
363 let d1 = "123.456".parse::<Decimal128Value>().expect("parse failed");
364 let d2 = "789.012".parse::<Decimal128Value>().expect("parse failed");
365 let sum = &d1 + &d2;
366 assert_eq!(sum.to_string(), "912.468");
367 }
368
369 #[test]
370 fn test_sub() {
371 let d1 = "789.012".parse::<Decimal128Value>().expect("parse failed");
372 let d2 = "123.456".parse::<Decimal128Value>().expect("parse failed");
373 let diff = &d1 - &d2;
374 assert_eq!(diff.to_string(), "665.556");
375 }
376
377 #[test]
378 fn test_mul() {
379 let d1 = "12.34".parse::<Decimal128Value>().expect("parse failed");
380 let d2 = "5.6".parse::<Decimal128Value>().expect("parse failed");
381 let product = &d1 * &d2;
382 assert_eq!(product.to_string(), "69.104");
383 }
384
385 #[test]
386 fn test_div() {
387 let d1 = "100.0".parse::<Decimal128Value>().expect("parse failed");
388 let d2 = "4.0".parse::<Decimal128Value>().expect("parse failed");
389 let quotient = &d1 / &d2;
390 assert_eq!(quotient.to_string(), "25");
391 }
392
393 #[test]
394 fn test_div_by_zero() {
395 let d1 = "100.0".parse::<Decimal128Value>().expect("parse failed");
396 let d2 = "0.0".parse::<Decimal128Value>().expect("parse failed");
397 let result = d1.div(&d2);
398 assert!(result.is_err());
399 assert_eq!(result.unwrap_err(), "division error");
400 }
401
402 #[test]
403 fn test_neg() {
404 let d = "123.456".parse::<Decimal128Value>().expect("parse failed");
405 let neg = -d.clone();
406 assert_eq!(neg.to_string(), "-123.456");
407 }
408
409 #[test]
410 fn test_abs() {
411 let d1 = "-123.456".parse::<Decimal128Value>().expect("parse failed");
412 let d2 = "123.456".parse::<Decimal128Value>().expect("parse failed");
413 assert_eq!(d1.abs().to_string(), "123.456");
414 assert_eq!(d2.abs().to_string(), "123.456");
415 }
416
417 #[test]
418 fn test_round_dp() {
419 let d = "123.456789"
420 .parse::<Decimal128Value>()
421 .expect("parse failed");
422 let rounded = d.round_dp(2);
423 assert_eq!(rounded.to_string(), "123.45");
424 }
425
426 #[test]
427 fn test_floor() {
428 let d = "123.789".parse::<Decimal128Value>().expect("parse failed");
429 let floored = d.floor();
430 assert_eq!(floored.to_string(), "123");
431 }
432
433 #[test]
434 fn test_ceil() {
435 let d = "123.789".parse::<Decimal128Value>().expect("parse failed");
436 let ceiled = d.ceil();
437 assert_eq!(ceiled.to_string(), "124");
438 }
439
440 #[test]
441 fn test_is_zero() {
442 let d = "0.0".parse::<Decimal128Value>().expect("parse failed");
443 assert!(d.is_zero());
444 }
445
446 #[test]
447 fn test_is_negative() {
448 let d = "-123.456".parse::<Decimal128Value>().expect("parse failed");
449 assert!(d.is_negative());
450 }
451
452 #[test]
453 fn test_is_positive() {
454 let d = "123.456".parse::<Decimal128Value>().expect("parse failed");
455 assert!(d.is_positive());
456 }
457
458 #[test]
459 fn test_finance_calculation() {
460 let price = "19.99".parse::<Decimal128Value>().expect("parse failed");
461 let quantity = "3".parse::<Decimal128Value>().expect("parse failed");
462 let tax_rate = "0.08".parse::<Decimal128Value>().expect("parse failed");
463
464 let subtotal = &price * &quantity;
465 let tax = &subtotal * &tax_rate;
466 let total = &subtotal + &tax;
467
468 assert_eq!(subtotal.to_string(), "59.97");
469 assert_eq!(tax.to_string(), "4.7976");
470 assert_eq!(total.to_string(), "64.7676");
471 }
472}