Skip to main content

substreams_database_change/numeric/
impls.rs

1use crate::numeric::{NumericAddable, NumericComparable, ToBigDecimal};
2use std::cmp::Ordering;
3use std::str::FromStr;
4use substreams::scalar::{BigDecimal, BigInt};
5
6// Macro for implementing ToBigDecimal and NumericAddable on integer types that have From<T> for BigDecimal
7macro_rules! impl_numeric_for_integer {
8    ($($t:ty),*) => {
9        $(
10            impl ToBigDecimal for $t {
11                fn to_big_decimal(&self) -> BigDecimal {
12                    BigDecimal::from(*self)
13                }
14            }
15
16            impl NumericAddable for $t {
17                fn add_assign_to(&self, target: &mut BigDecimal) {
18                    *target += BigDecimal::from(*self);
19                }
20
21                fn sub_assign_from(&self, target: &mut BigDecimal) {
22                    *target -= BigDecimal::from(*self);
23                }
24            }
25        )*
26    };
27}
28
29// Macro for implementing ToBigDecimal and NumericAddable on integer types that need casting
30macro_rules! impl_numeric_for_integer_via_cast {
31    ($($t:ty => $cast:ty),*) => {
32        $(
33            impl ToBigDecimal for $t {
34                fn to_big_decimal(&self) -> BigDecimal {
35                    BigDecimal::from(*self as $cast)
36                }
37            }
38
39            impl NumericAddable for $t {
40                fn add_assign_to(&self, target: &mut BigDecimal) {
41                    *target += BigDecimal::from(*self as $cast);
42                }
43
44                fn sub_assign_from(&self, target: &mut BigDecimal) {
45                    *target -= BigDecimal::from(*self as $cast);
46                }
47            }
48        )*
49    };
50}
51
52// Apply to integer types that have From<T> implementations for BigDecimal
53impl_numeric_for_integer!(i32, i64, u32, u64);
54
55// Apply to smaller integer types that need casting via i64/u64
56impl_numeric_for_integer_via_cast!(
57    i8 => i64,
58    i16 => i64,
59    u8 => u64,
60    u16 => u64,
61    isize => i64,
62    usize => u64
63);
64
65// i128 and u128 - convert via string since BigInt doesn't support them directly
66impl ToBigDecimal for i128 {
67    fn to_big_decimal(&self) -> BigDecimal {
68        BigDecimal::from_str(&self.to_string())
69            .expect("i128 should always convert to BigDecimal")
70    }
71}
72
73impl NumericAddable for i128 {
74    fn add_assign_to(&self, target: &mut BigDecimal) {
75        *target += self.to_big_decimal();
76    }
77
78    fn sub_assign_from(&self, target: &mut BigDecimal) {
79        *target -= self.to_big_decimal();
80    }
81}
82
83impl ToBigDecimal for u128 {
84    fn to_big_decimal(&self) -> BigDecimal {
85        BigDecimal::from_str(&self.to_string())
86            .expect("u128 should always convert to BigDecimal")
87    }
88}
89
90impl NumericAddable for u128 {
91    fn add_assign_to(&self, target: &mut BigDecimal) {
92        *target += self.to_big_decimal();
93    }
94
95    fn sub_assign_from(&self, target: &mut BigDecimal) {
96        *target -= self.to_big_decimal();
97    }
98}
99
100impl ToBigDecimal for BigDecimal {
101    fn to_big_decimal(&self) -> BigDecimal {
102        self.clone()
103    }
104}
105
106impl NumericAddable for BigDecimal {
107    fn add_assign_to(&self, target: &mut BigDecimal) {
108        *target += self;
109    }
110
111    fn sub_assign_from(&self, target: &mut BigDecimal) {
112        *target -= self;
113    }
114}
115
116impl ToBigDecimal for &BigDecimal {
117    fn to_big_decimal(&self) -> BigDecimal {
118        (*self).clone()
119    }
120}
121
122impl NumericAddable for &BigDecimal {
123    fn add_assign_to(&self, target: &mut BigDecimal) {
124        *target += *self;
125    }
126
127    fn sub_assign_from(&self, target: &mut BigDecimal) {
128        *target -= *self;
129    }
130}
131
132impl ToBigDecimal for BigInt {
133    fn to_big_decimal(&self) -> BigDecimal {
134        BigDecimal::from(self.clone())
135    }
136}
137
138impl NumericAddable for BigInt {
139    fn add_assign_to(&self, target: &mut BigDecimal) {
140        *target += BigDecimal::from(self.clone());
141    }
142
143    fn sub_assign_from(&self, target: &mut BigDecimal) {
144        *target -= BigDecimal::from(self.clone());
145    }
146}
147
148impl ToBigDecimal for &BigInt {
149    fn to_big_decimal(&self) -> BigDecimal {
150        BigDecimal::from((*self).clone())
151    }
152}
153
154impl NumericAddable for &BigInt {
155    fn add_assign_to(&self, target: &mut BigDecimal) {
156        *target += BigDecimal::from((*self).clone());
157    }
158
159    fn sub_assign_from(&self, target: &mut BigDecimal) {
160        *target -= BigDecimal::from((*self).clone());
161    }
162}
163
164// Macro for implementing ToBigDecimal and NumericAddable for string types
165macro_rules! impl_numeric_for_string {
166    ($($t:ty),*) => {
167        $(
168            impl ToBigDecimal for $t {
169                fn to_big_decimal(&self) -> BigDecimal {
170                    BigDecimal::from_str(self).unwrap_or_else(|_| {
171                        panic!(
172                            "add/sub() requires a valid numeric value, got: {}",
173                            self
174                        )
175                    })
176                }
177            }
178
179            impl NumericAddable for $t {
180                fn add_assign_to(&self, target: &mut BigDecimal) {
181                    let value = self.to_big_decimal();
182                    *target += value;
183                }
184
185                fn sub_assign_from(&self, target: &mut BigDecimal) {
186                    let value = self.to_big_decimal();
187                    *target -= value;
188                }
189            }
190        )*
191    };
192}
193
194// Apply to String and &str types
195impl_numeric_for_string!(String, &str);
196
197// ============================================================
198// NumericComparable implementations
199// ============================================================
200
201// Macro for implementing NumericComparable on integer types using BigDecimal's native PartialOrd
202macro_rules! impl_numeric_comparable_for_integer {
203    ($($t:ty),*) => {
204        $(
205            impl NumericComparable for $t {
206                fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
207                    // Direct comparison using BigDecimal's PartialOrd<$t>
208                    // This is zero-allocation for <type>
209                    self.partial_cmp(other)
210                        .expect(concat!("BigDecimal comparison should always succeed for ", stringify!($t)))
211                }
212            }
213        )*
214    };
215}
216
217// Apply to all primitive integer types including i128/u128
218impl_numeric_comparable_for_integer!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
219
220// Specialized implementations for isize/usize (cast to i64/u64)
221impl NumericComparable for isize {
222    fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
223        (*self as i64).partial_cmp(other)
224            .expect("BigDecimal comparison should always succeed for isize")
225    }
226}
227
228impl NumericComparable for usize {
229    fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
230        (*self as u64).partial_cmp(other)
231            .expect("BigDecimal comparison should always succeed for usize")
232    }
233}
234
235// BigDecimal implementations
236impl NumericComparable for BigDecimal {
237    fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
238        self.cmp(other)
239    }
240}
241
242impl NumericComparable for &BigDecimal {
243    fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
244        (*self).cmp(other)
245    }
246}
247
248// BigInt implementations - convert to BigDecimal for comparison
249impl NumericComparable for BigInt {
250    fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
251        let self_bd = BigDecimal::from(self.clone());
252        self_bd.cmp(other)
253    }
254}
255
256impl NumericComparable for &BigInt {
257    fn cmp_to_big_decimal(&self, other: &BigDecimal) -> Ordering {
258        let self_bd = BigDecimal::from((*self).clone());
259        self_bd.cmp(other)
260    }
261}