Skip to main content

reifydb_value/value/decimal/
mod.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 ReifyDB
3
4use std::{
5	cmp::Ordering,
6	fmt,
7	fmt::{Display, Formatter},
8	hash,
9	ops::{Add, Deref, Div, Mul, Sub},
10	str::FromStr,
11};
12
13use bigdecimal::{BigDecimal as BigDecimalInner, FromPrimitive};
14use num_traits::{One, Zero};
15use serde::{
16	Deserialize, Deserializer, Serialize, Serializer,
17	de::{self, Visitor},
18};
19
20use super::{int::Int, uint::Uint};
21use crate::{
22	error::{Error, TypeError},
23	fragment::Fragment,
24	value::value_type::ValueType,
25};
26
27pub mod parse;
28
29#[repr(transparent)]
30#[derive(Clone, Debug)]
31pub struct Decimal(pub BigDecimalInner);
32
33impl Decimal {
34	pub fn zero() -> Self {
35		Self(BigDecimalInner::zero())
36	}
37
38	pub fn one() -> Self {
39		Self(BigDecimalInner::one())
40	}
41}
42
43impl Deref for Decimal {
44	type Target = BigDecimalInner;
45
46	fn deref(&self) -> &Self::Target {
47		&self.0
48	}
49}
50
51impl Decimal {
52	pub fn new(value: BigDecimalInner) -> Self {
53		Self(value)
54	}
55
56	pub fn from_bigdecimal(value: BigDecimalInner) -> Self {
57		Self(value)
58	}
59
60	pub fn with_scale(value: BigDecimalInner, scale: i64) -> Self {
61		Self(value.with_scale(scale))
62	}
63
64	pub fn from_i64(value: i64) -> Self {
65		Self(BigDecimalInner::from(value))
66	}
67
68	pub fn inner(&self) -> &BigDecimalInner {
69		&self.0
70	}
71
72	pub fn to_bigdecimal(self) -> BigDecimalInner {
73		self.0
74	}
75
76	pub fn negate(self) -> Self {
77		Self(-self.0)
78	}
79}
80
81impl PartialEq for Decimal {
82	fn eq(&self, other: &Self) -> bool {
83		self.0 == other.0
84	}
85}
86
87impl Eq for Decimal {}
88
89impl PartialOrd for Decimal {
90	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
91		Some(self.cmp(other))
92	}
93}
94
95impl Ord for Decimal {
96	fn cmp(&self, other: &Self) -> Ordering {
97		self.0.cmp(&other.0)
98	}
99}
100
101impl Display for Decimal {
102	fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
103		self.0.fmt(f)
104	}
105}
106
107impl hash::Hash for Decimal {
108	fn hash<H: hash::Hasher>(&self, state: &mut H) {
109		self.0.to_string().hash(state);
110	}
111}
112
113impl FromStr for Decimal {
114	type Err = Error;
115
116	fn from_str(s: &str) -> Result<Self, Self::Err> {
117		let big_decimal = BigDecimalInner::from_str(s).map_err(|_| -> Error {
118			TypeError::InvalidNumberFormat {
119				target: ValueType::Decimal,
120				fragment: Fragment::None,
121			}
122			.into()
123		})?;
124
125		Ok(Self(big_decimal))
126	}
127}
128
129impl From<i64> for Decimal {
130	fn from(value: i64) -> Self {
131		Self(BigDecimalInner::from(value))
132	}
133}
134
135impl From<i8> for Decimal {
136	fn from(value: i8) -> Self {
137		Self::from(value as i64)
138	}
139}
140
141impl From<i16> for Decimal {
142	fn from(value: i16) -> Self {
143		Self::from(value as i64)
144	}
145}
146
147impl From<i32> for Decimal {
148	fn from(value: i32) -> Self {
149		Self::from(value as i64)
150	}
151}
152
153impl From<i128> for Decimal {
154	fn from(value: i128) -> Self {
155		Self(BigDecimalInner::from(value))
156	}
157}
158
159impl From<u8> for Decimal {
160	fn from(value: u8) -> Self {
161		Self::from(value as i64)
162	}
163}
164
165impl From<u16> for Decimal {
166	fn from(value: u16) -> Self {
167		Self::from(value as i64)
168	}
169}
170
171impl From<u32> for Decimal {
172	fn from(value: u32) -> Self {
173		Self::from(value as i64)
174	}
175}
176
177impl From<u64> for Decimal {
178	fn from(value: u64) -> Self {
179		Self(BigDecimalInner::from(value))
180	}
181}
182
183impl From<u128> for Decimal {
184	fn from(value: u128) -> Self {
185		Self(BigDecimalInner::from(value))
186	}
187}
188
189impl From<f32> for Decimal {
190	fn from(value: f32) -> Self {
191		if !value.is_finite() {
192			return Self(BigDecimalInner::from(0));
193		}
194		let inner = BigDecimalInner::from_str(&value.to_string()).unwrap_or_else(|_| {
195			BigDecimalInner::from_f32(value).unwrap_or_else(|| BigDecimalInner::from(0))
196		});
197		Self(inner)
198	}
199}
200
201impl From<f64> for Decimal {
202	fn from(value: f64) -> Self {
203		if !value.is_finite() {
204			return Self(BigDecimalInner::from(0));
205		}
206		let inner = BigDecimalInner::from_str(&value.to_string()).unwrap_or_else(|_| {
207			BigDecimalInner::from_f64(value).unwrap_or_else(|| BigDecimalInner::from(0))
208		});
209		Self(inner)
210	}
211}
212
213impl From<BigDecimalInner> for Decimal {
214	fn from(value: BigDecimalInner) -> Self {
215		Self(value)
216	}
217}
218
219impl From<Int> for Decimal {
220	fn from(value: Int) -> Self {
221		Self(BigDecimalInner::from_bigint(value.0, 0))
222	}
223}
224
225impl From<Uint> for Decimal {
226	fn from(value: Uint) -> Self {
227		Self(BigDecimalInner::from_bigint(value.0, 0))
228	}
229}
230
231impl Add for Decimal {
232	type Output = Self;
233
234	fn add(self, rhs: Self) -> Self::Output {
235		Self(self.0 + rhs.0)
236	}
237}
238
239impl Sub for Decimal {
240	type Output = Self;
241
242	fn sub(self, rhs: Self) -> Self::Output {
243		Self(self.0 - rhs.0)
244	}
245}
246
247impl Mul for Decimal {
248	type Output = Self;
249
250	fn mul(self, rhs: Self) -> Self::Output {
251		Self(self.0 * rhs.0)
252	}
253}
254
255impl Div for Decimal {
256	type Output = Self;
257
258	fn div(self, rhs: Self) -> Self::Output {
259		Self(self.0 / rhs.0)
260	}
261}
262
263impl Add<&Decimal> for &Decimal {
264	type Output = Decimal;
265
266	fn add(self, rhs: &Decimal) -> Self::Output {
267		Decimal(&self.0 + &rhs.0)
268	}
269}
270
271impl Sub<&Decimal> for &Decimal {
272	type Output = Decimal;
273
274	fn sub(self, rhs: &Decimal) -> Self::Output {
275		Decimal(&self.0 - &rhs.0)
276	}
277}
278
279impl Mul<&Decimal> for &Decimal {
280	type Output = Decimal;
281
282	fn mul(self, rhs: &Decimal) -> Self::Output {
283		Decimal(&self.0 * &rhs.0)
284	}
285}
286
287impl Div<&Decimal> for &Decimal {
288	type Output = Decimal;
289
290	fn div(self, rhs: &Decimal) -> Self::Output {
291		Decimal(&self.0 / &rhs.0)
292	}
293}
294
295impl Default for Decimal {
296	fn default() -> Self {
297		Self::zero()
298	}
299}
300
301impl Serialize for Decimal {
302	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
303	where
304		S: Serializer,
305	{
306		serializer.serialize_str(&self.0.to_string())
307	}
308}
309
310struct DecimalVisitor;
311
312impl<'de> Visitor<'de> for DecimalVisitor {
313	type Value = Decimal;
314
315	fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
316		formatter.write_str("a decimal number as a string")
317	}
318
319	fn visit_str<E>(self, value: &str) -> Result<Decimal, E>
320	where
321		E: de::Error,
322	{
323		BigDecimalInner::from_str(value).map(Decimal).map_err(|e| E::custom(format!("invalid decimal: {}", e)))
324	}
325}
326
327impl<'de> Deserialize<'de> for Decimal {
328	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
329	where
330		D: Deserializer<'de>,
331	{
332		deserializer.deserialize_str(DecimalVisitor)
333	}
334}
335
336#[cfg(test)]
337pub mod tests {
338	use postcard::{from_bytes, to_stdvec};
339	use serde_json::{from_str, to_string};
340
341	use super::*;
342
343	#[test]
344	fn test_new_decimal_valid() {
345		let bd = BigDecimalInner::from_str("123.45").unwrap();
346		let decimal = Decimal::new(bd);
347		assert_eq!(decimal.to_string(), "123.45");
348	}
349
350	#[test]
351	fn test_from_str() {
352		let decimal = Decimal::from_str("123.45").unwrap();
353		assert_eq!(decimal.to_string(), "123.45");
354	}
355
356	#[test]
357	fn test_comparison() {
358		let d1 = Decimal::from_str("123.45").unwrap();
359		let d2 = Decimal::from_str("123.46").unwrap();
360		let d3 = Decimal::from_str("123.45").unwrap();
361
362		assert!(d1 < d2);
363		assert_eq!(d1, d3);
364	}
365
366	#[test]
367	fn test_display() {
368		let decimal = Decimal::from_str("123.45").unwrap();
369		assert_eq!(format!("{}", decimal), "123.45");
370	}
371
372	#[test]
373	fn test_serde_json() {
374		let decimal = Decimal::from_str("123.456789").unwrap();
375		let json = to_string(&decimal).unwrap();
376		assert_eq!(json, "\"123.456789\"");
377
378		let deserialized: Decimal = from_str(&json).unwrap();
379		assert_eq!(deserialized, decimal);
380	}
381
382	#[test]
383	fn test_serde_json_negative() {
384		let decimal = Decimal::from_str("-987.654321").unwrap();
385		let json = to_string(&decimal).unwrap();
386		assert_eq!(json, "\"-987.654321\"");
387
388		let deserialized: Decimal = from_str(&json).unwrap();
389		assert_eq!(deserialized, decimal);
390	}
391
392	#[test]
393	fn test_serde_json_zero() {
394		let decimal = Decimal::zero();
395		let json = to_string(&decimal).unwrap();
396		assert_eq!(json, "\"0\"");
397
398		let deserialized: Decimal = from_str(&json).unwrap();
399		assert_eq!(deserialized, decimal);
400	}
401
402	#[test]
403	fn test_serde_json_high_precision() {
404		let decimal = Decimal::from_str("123456789.123456789123456789").unwrap();
405		let json = to_string(&decimal).unwrap();
406
407		let deserialized: Decimal = from_str(&json).unwrap();
408		assert_eq!(deserialized, decimal);
409	}
410
411	#[test]
412	fn test_serde_postcard() {
413		let decimal = Decimal::from_str("123.456789").unwrap();
414		let encoded = to_stdvec(&decimal).unwrap();
415
416		let decoded: Decimal = from_bytes(&encoded).unwrap();
417		assert_eq!(decoded, decimal);
418	}
419
420	#[test]
421	fn test_serde_postcard_negative() {
422		let decimal = Decimal::from_str("-987.654321").unwrap();
423		let encoded = to_stdvec(&decimal).unwrap();
424
425		let decoded: Decimal = from_bytes(&encoded).unwrap();
426		assert_eq!(decoded, decimal);
427	}
428
429	#[test]
430	fn test_serde_postcard_zero() {
431		let decimal = Decimal::zero();
432		let encoded = to_stdvec(&decimal).unwrap();
433
434		let decoded: Decimal = from_bytes(&encoded).unwrap();
435		assert_eq!(decoded, decimal);
436	}
437
438	#[test]
439	fn test_serde_postcard_high_precision() {
440		let decimal = Decimal::from_str("123456789.123456789123456789").unwrap();
441		let encoded = to_stdvec(&decimal).unwrap();
442
443		let decoded: Decimal = from_bytes(&encoded).unwrap();
444		assert_eq!(decoded, decimal);
445	}
446
447	#[test]
448	fn test_serde_postcard_large_number() {
449		let decimal = Decimal::from_str("999999999999999999999999999999.999999999999999999999999").unwrap();
450		let encoded = to_stdvec(&decimal).unwrap();
451
452		let decoded: Decimal = from_bytes(&encoded).unwrap();
453		assert_eq!(decoded, decimal);
454	}
455}