fluidattacks_database_core_domain/cell/
numeric.rs1use 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}