reifydb_type/value/decimal/
mod.rs

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