Skip to main content

fluidattacks_database_core_domain/cell/
numeric.rs

1use super::cell_type::CellType;
2use super::field_type::{FieldType, IntoValue};
3use super::value::{valid_decimal, Decimal, DecimalError, RawValue};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct Numeric<const P: u8, const S: u8> {
7    value: Decimal,
8}
9
10impl<const P: u8, const S: u8> Numeric<P, S> {
11    const LIMITS: () = assert!(
12        valid_decimal(P, S),
13        "Numeric needs 1 <= P <= 38, S <= 37 and S <= P"
14    );
15
16    pub fn new(unscaled: i128) -> Result<Self, DecimalError> {
17        let () = Self::LIMITS;
18        let fits = 10_u128
19            .checked_pow(u32::from(P))
20            .is_some_and(|limit| unscaled.unsigned_abs() < limit);
21        if fits {
22            Decimal::new(unscaled, S).map(|value| Self { value })
23        } else {
24            Err(DecimalError::TooManyDigits { precision: P })
25        }
26    }
27
28    #[must_use]
29    pub const fn unscaled(&self) -> i128 {
30        self.value.unscaled()
31    }
32}
33
34impl<const P: u8, const S: u8> FieldType for Numeric<P, S> {
35    fn cell_type() -> CellType {
36        let () = Self::LIMITS;
37        CellType::Decimal {
38            precision: P,
39            scale: S,
40        }
41    }
42}
43
44impl<const P: u8, const S: u8> IntoValue for Numeric<P, S> {
45    fn into_value(self) -> RawValue {
46        RawValue::Decimal(self.value)
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::Numeric;
53    use crate::cell::cell_type::CellType;
54    use crate::cell::field_type::{FieldType, IntoValue};
55    use crate::cell::value::{Decimal, DecimalError, RawValue};
56
57    #[test]
58    fn numeric_is_a_decimal_column_of_its_precision_and_scale() {
59        assert_eq!(
60            Numeric::<12, 2>::cell_type(),
61            CellType::Decimal {
62                precision: 12,
63                scale: 2
64            }
65        );
66        assert_eq!(
67            Numeric::<12, 2>::new(12_345).map(IntoValue::into_value),
68            Decimal::new(12_345, 2).map(RawValue::Decimal)
69        );
70    }
71
72    #[test]
73    fn numeric_rejects_values_over_its_precision() {
74        assert_eq!(
75            Numeric::<4, 2>::new(-9_999).map(|n| n.unscaled()),
76            Ok(-9_999)
77        );
78        assert_eq!(
79            Numeric::<4, 2>::new(10_000),
80            Err(DecimalError::TooManyDigits { precision: 4 })
81        );
82        assert_eq!(
83            Numeric::<38, 0>::new(i128::MIN),
84            Err(DecimalError::TooManyDigits { precision: 38 })
85        );
86    }
87}