proof_of_sql/base/math/
big_decimal_ext.rs1use super::decimal::{IntermediateDecimalError, IntermediateDecimalError::LossyCast};
2use bigdecimal::BigDecimal;
3use num_bigint::BigInt;
4
5pub trait BigDecimalExt {
7 fn precision(&self) -> u64;
9 fn scale(&self) -> i64;
11 fn try_into_bigint_with_precision_and_scale(
13 &self,
14 precision: u8,
15 scale: i8,
16 ) -> Result<BigInt, IntermediateDecimalError>;
17}
18impl BigDecimalExt for BigDecimal {
19 fn precision(&self) -> u64 {
21 self.normalized().digits()
22 }
23
24 fn scale(&self) -> i64 {
26 self.normalized().fractional_digit_count()
27 }
28
29 fn try_into_bigint_with_precision_and_scale(
35 &self,
36 precision: u8,
37 scale: i8,
38 ) -> Result<BigInt, IntermediateDecimalError> {
39 if self.scale() > scale.into() {
40 Err(IntermediateDecimalError::ConversionFailure)?;
41 }
42 let scaled_decimal = self.normalized().with_scale(scale.into());
43 if scaled_decimal.digits() > precision.into() {
44 return Err(LossyCast);
45 }
46 let (d, _) = scaled_decimal.into_bigint_and_exponent();
47 Ok(d)
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 use super::*;
54 use alloc::string::ToString;
55
56 #[test]
57 fn test_valid_decimal_simple() {
58 let decimal = "123.45".parse::<BigDecimal>();
59 assert!(decimal.is_ok());
60 let unwrapped_decimal: BigDecimal = decimal.unwrap().normalized();
61 assert_eq!(unwrapped_decimal.to_string(), "123.45");
62 assert_eq!(unwrapped_decimal.precision(), 5);
63 assert_eq!(unwrapped_decimal.scale(), 2);
64 }
65
66 #[test]
67 fn test_valid_decimal_with_leading_and_trailing_zeros() {
68 let decimal = "000123.45000".parse::<BigDecimal>();
69 assert!(decimal.is_ok());
70 let unwrapped_decimal: BigDecimal = decimal.unwrap().normalized();
71 assert_eq!(unwrapped_decimal.to_string(), "123.45");
72 assert_eq!(unwrapped_decimal.precision(), 5);
73 assert_eq!(unwrapped_decimal.scale(), 2);
74 }
75
76 #[test]
77 fn test_accessors() {
78 let decimal: BigDecimal = "123.456".parse::<BigDecimal>().unwrap().normalized();
79 assert_eq!(decimal.to_string(), "123.456");
80 assert_eq!(decimal.precision(), 6);
81 assert_eq!(decimal.scale(), 3);
82 }
83
84 #[test]
85 fn we_can_catch_conversion_error() {
86 let big_decimal: BigDecimal = "123.456".parse::<BigDecimal>().unwrap().normalized();
87 let err = big_decimal
88 .try_into_bigint_with_precision_and_scale(2, 1)
89 .unwrap_err();
90 assert!(matches!(err, IntermediateDecimalError::ConversionFailure));
91 }
92}