icydb_model/base/normalizer/
num.rs1use crate::{
8 base::helper::try_cast_decimal, prelude::*, schema::NumericValue, visitor::Normalizer,
9};
10use std::any::type_name;
11
12#[normalizer]
17pub struct Clamp {
18 min: Decimal,
19 max: Decimal,
20 invalid_configuration: bool,
21}
22
23impl Clamp {
24 pub fn new<N: NumericValue>(min: N, max: N) -> Self {
26 let min = try_cast_decimal(&min);
27 let max = try_cast_decimal(&max);
28
29 Self {
32 min: min.unwrap_or_default(),
33 max: max.unwrap_or_default(),
34 invalid_configuration: min.is_none() || max.is_none(),
35 }
36 }
37}
38
39impl<T: NumericValue> Normalizer<T> for Clamp {
40 fn normalize(&self, value: &mut T) -> Result<(), String> {
41 if self.invalid_configuration {
42 return Err("Clamp bounds cannot be represented as Decimal".to_string());
43 }
44 if self.min > self.max {
45 return Err(format!(
46 "Clamp requires min <= max (got {}..={})",
47 self.min, self.max
48 ));
49 }
50
51 let v = try_cast_decimal(value).ok_or_else(|| {
52 format!(
53 "value of type {} cannot be represented as Decimal",
54 type_name::<T>()
55 )
56 })?;
57
58 let clamped = if v < self.min {
59 self.min
60 } else if v > self.max {
61 self.max
62 } else {
63 v
64 };
65
66 *value = T::try_from_decimal(clamped).ok_or_else(|| {
67 format!(
68 "clamped value cannot be represented as {}",
69 type_name::<T>()
70 )
71 })?;
72
73 Ok(())
74 }
75}
76
77#[normalizer]
82pub struct RoundDecimalPlaces {
83 scale: u32,
84 invalid_configuration: bool,
85}
86
87impl RoundDecimalPlaces {
88 #[must_use]
90 pub fn new(scale: impl TryInto<u32>) -> Self {
91 let scale = scale.try_into().ok();
92 Self {
93 scale: scale.unwrap_or_default(),
94 invalid_configuration: scale.is_none(),
95 }
96 }
97}
98
99impl Normalizer<Decimal> for RoundDecimalPlaces {
100 fn normalize(&self, value: &mut Decimal) -> Result<(), String> {
101 if self.invalid_configuration {
102 return Err("decimal rounding scale cannot be represented as u32".to_string());
103 }
104 *value = value.round_dp(self.scale);
105
106 Ok(())
107 }
108}
109
110#[cfg(test)]
115mod tests;