fluidattacks_database_core_domain/cell/
value.rs1use alloc::string::String;
2use alloc::vec::Vec;
3use core::fmt;
4
5use chrono::{DateTime, NaiveDate, Utc};
6
7pub const MAX_PRECISION: u8 = 38;
9
10pub const MAX_SCALE: u8 = 37;
12
13#[must_use]
14pub const fn valid_decimal(precision: u8, scale: u8) -> bool {
15 precision >= 1 && precision <= MAX_PRECISION && scale <= MAX_SCALE && scale <= precision
16}
17
18const MAX_UNSCALED: u128 = 99_999_999_999_999_999_999_999_999_999_999_999_999;
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum DecimalError {
22 TooManyDigits { precision: u8 },
23 ScaleTooLarge,
24}
25
26impl fmt::Display for DecimalError {
27 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
28 match self {
29 Self::TooManyDigits { precision } => {
30 write!(f, "decimal has more than {precision} digits")
31 }
32 Self::ScaleTooLarge => write!(f, "decimal scale is over {MAX_SCALE}"),
33 }
34 }
35}
36
37impl core::error::Error for DecimalError {}
38
39#[derive(Debug, Clone, PartialEq, Eq)]
42pub struct Decimal {
43 unscaled: i128,
44 scale: u8,
45}
46
47impl Decimal {
48 pub const fn new(unscaled: i128, scale: u8) -> Result<Self, DecimalError> {
49 if unscaled.unsigned_abs() > MAX_UNSCALED {
50 Err(DecimalError::TooManyDigits {
51 precision: MAX_PRECISION,
52 })
53 } else if scale > MAX_SCALE {
54 Err(DecimalError::ScaleTooLarge)
55 } else {
56 Ok(Self { unscaled, scale })
57 }
58 }
59
60 #[must_use]
61 pub const fn unscaled(&self) -> i128 {
62 self.unscaled
63 }
64
65 #[must_use]
66 pub const fn scale(&self) -> u8 {
67 self.scale
68 }
69}
70
71#[derive(Debug, Clone, PartialEq)]
73pub enum RawValue {
74 Null,
75 Text(String),
76 Integer(i64),
77 Decimal(Decimal),
78 Float(f64),
79 Boolean(bool),
80 Date(NaiveDate),
81 Timestamp(DateTime<Utc>),
82 Array(Vec<Self>),
83 Json(serde_json::Value),
84}
85
86#[cfg(test)]
87mod tests {
88 use super::{Decimal, DecimalError};
89
90 const MAX: i128 = 99_999_999_999_999_999_999_999_999_999_999_999_999;
91
92 #[test]
93 fn decimal_keeps_38_digits_either_sign() {
94 assert_eq!(Decimal::new(MAX, 10).map(|d| d.unscaled()), Ok(MAX));
95 assert_eq!(Decimal::new(-MAX, 0).map(|d| d.unscaled()), Ok(-MAX));
96 }
97
98 #[test]
99 fn decimal_rejects_39_digits() {
100 assert_eq!(
101 Decimal::new(100_000_000_000_000_000_000_000_000_000_000_000_000, 0),
102 Err(DecimalError::TooManyDigits { precision: 38 })
103 );
104 assert_eq!(
105 Decimal::new(-100_000_000_000_000_000_000_000_000_000_000_000_000, 0),
106 Err(DecimalError::TooManyDigits { precision: 38 })
107 );
108 assert_eq!(
109 Decimal::new(i128::MIN, 0),
110 Err(DecimalError::TooManyDigits { precision: 38 })
111 );
112 }
113
114 #[test]
115 fn decimal_rejects_scale_over_37() {
116 assert_eq!(Decimal::new(1, 37).map(|d| d.scale()), Ok(37));
117 assert_eq!(Decimal::new(1, 38), Err(DecimalError::ScaleTooLarge));
118 }
119}