cassandra_protocol/types/
decimal.rs

1use derive_more::Constructor;
2use float_eq::*;
3use num_bigint::BigInt;
4use std::io::Cursor;
5
6use crate::frame::{Serialize, Version};
7
8/// Cassandra Decimal type
9#[derive(Debug, Clone, PartialEq, Constructor, Ord, PartialOrd, Eq, Hash)]
10pub struct Decimal {
11    pub unscaled: BigInt,
12    pub scale: i32,
13}
14
15impl Decimal {
16    /// Method that returns plain `BigInt` value.
17    pub fn as_plain(&self) -> BigInt {
18        self.unscaled.clone() / 10i64.pow(self.scale as u32)
19    }
20}
21
22impl Serialize for Decimal {
23    fn serialize(&self, cursor: &mut Cursor<&mut Vec<u8>>, version: Version) {
24        self.scale.serialize(cursor, version);
25        self.unscaled
26            .to_signed_bytes_be()
27            .serialize(cursor, version);
28    }
29}
30
31macro_rules! impl_from_for_decimal {
32    ($t:ty) => {
33        impl From<$t> for Decimal {
34            fn from(i: $t) -> Self {
35                Decimal {
36                    unscaled: i.into(),
37                    scale: 0,
38                }
39            }
40        }
41    };
42}
43
44impl_from_for_decimal!(i8);
45impl_from_for_decimal!(i16);
46impl_from_for_decimal!(i32);
47impl_from_for_decimal!(i64);
48impl_from_for_decimal!(u8);
49impl_from_for_decimal!(u16);
50
51impl From<f32> for Decimal {
52    fn from(f: f32) -> Decimal {
53        let mut scale = 0;
54
55        loop {
56            let unscaled = f * (10i64.pow(scale) as f32);
57
58            if float_eq!(unscaled, unscaled.trunc(), abs <= f32::EPSILON) {
59                return Decimal::new((unscaled as i64).into(), scale as i32);
60            }
61
62            scale += 1;
63        }
64    }
65}
66
67impl From<f64> for Decimal {
68    fn from(f: f64) -> Decimal {
69        let mut scale = 0;
70
71        loop {
72            let unscaled = f * (10i64.pow(scale) as f64);
73
74            if float_eq!(unscaled, unscaled.trunc(), abs <= f64::EPSILON) {
75                return Decimal::new((unscaled as i64).into(), scale as i32);
76            }
77
78            scale += 1;
79        }
80    }
81}
82
83impl From<Decimal> for BigInt {
84    fn from(value: Decimal) -> Self {
85        value.as_plain()
86    }
87}
88
89#[cfg(test)]
90mod test {
91    use super::*;
92
93    #[test]
94    fn serialize_test() {
95        assert_eq!(
96            Decimal::new(129.into(), 0).serialize_to_vec(Version::V4),
97            vec![0, 0, 0, 0, 0x00, 0x81]
98        );
99
100        assert_eq!(
101            Decimal::new(BigInt::from(-129), 0).serialize_to_vec(Version::V4),
102            vec![0, 0, 0, 0, 0xFF, 0x7F]
103        );
104
105        let expected: Vec<u8> = vec![0, 0, 0, 1, 0x00, 0x81];
106        assert_eq!(
107            Decimal::new(129.into(), 1).serialize_to_vec(Version::V4),
108            expected
109        );
110
111        let expected: Vec<u8> = vec![0, 0, 0, 1, 0xFF, 0x7F];
112        assert_eq!(
113            Decimal::new(BigInt::from(-129), 1).serialize_to_vec(Version::V4),
114            expected
115        );
116    }
117
118    #[test]
119    fn from_f32() {
120        assert_eq!(
121            Decimal::from(12300001_f32),
122            Decimal::new(12300001.into(), 0)
123        );
124        assert_eq!(
125            Decimal::from(1230000.1_f32),
126            Decimal::new(12300001.into(), 1)
127        );
128        assert_eq!(
129            Decimal::from(0.12300001_f32),
130            Decimal::new(12300001.into(), 8)
131        );
132    }
133
134    #[test]
135    fn from_f64() {
136        assert_eq!(
137            Decimal::from(1230000000000001_f64),
138            Decimal::new(1230000000000001i64.into(), 0)
139        );
140        assert_eq!(
141            Decimal::from(123000000000000.1f64),
142            Decimal::new(1230000000000001i64.into(), 1)
143        );
144        assert_eq!(
145            Decimal::from(0.1230000000000001f64),
146            Decimal::new(1230000000000001i64.into(), 16)
147        );
148    }
149}