ethereum_mysql/sql_uint/
primitive_ops.rs

1//! Operations between SqlU256 and primitive types
2//!
3//! This module provides convenient arithmetic operations between SqlU256 and Rust's
4//! primitive integer types, supporting both directions (e.g., `value * 2` and `2 * value`).
5//! This eliminates the need for explicit conversions like `value * SqlU256::from(2)`.
6
7use super::{SqlU256, U256};
8use std::ops::{Add, Div, Mul, Rem, Sub};
9
10/// Macro to implement arithmetic operations between SqlU256 and primitive types in both directions
11macro_rules! impl_primitive_ops {
12    ($prim_type:ty) => {
13        // SqlU256 op primitive (e.g., value * 2)
14        impl Add<$prim_type> for SqlU256 {
15            type Output = Self;
16
17            fn add(self, rhs: $prim_type) -> Self::Output {
18                SqlU256::from(self.0 + U256::from(rhs))
19            }
20        }
21
22        impl Sub<$prim_type> for SqlU256 {
23            type Output = Self;
24
25            fn sub(self, rhs: $prim_type) -> Self::Output {
26                SqlU256::from(self.0 - U256::from(rhs))
27            }
28        }
29
30        impl Mul<$prim_type> for SqlU256 {
31            type Output = Self;
32
33            fn mul(self, rhs: $prim_type) -> Self::Output {
34                SqlU256::from(self.0 * U256::from(rhs))
35            }
36        }
37
38        impl Div<$prim_type> for SqlU256 {
39            type Output = Self;
40
41            fn div(self, rhs: $prim_type) -> Self::Output {
42                SqlU256::from(self.0 / U256::from(rhs))
43            }
44        }
45
46        impl Rem<$prim_type> for SqlU256 {
47            type Output = Self;
48
49            fn rem(self, rhs: $prim_type) -> Self::Output {
50                SqlU256::from(self.0 % U256::from(rhs))
51            }
52        }
53
54        // primitive op SqlU256 (e.g., 2 * value)
55        impl Add<SqlU256> for $prim_type {
56            type Output = SqlU256;
57
58            fn add(self, rhs: SqlU256) -> Self::Output {
59                SqlU256::from(U256::from(self) + rhs.0)
60            }
61        }
62
63        impl Sub<SqlU256> for $prim_type {
64            type Output = SqlU256;
65
66            fn sub(self, rhs: SqlU256) -> Self::Output {
67                SqlU256::from(U256::from(self) - rhs.0)
68            }
69        }
70
71        impl Mul<SqlU256> for $prim_type {
72            type Output = SqlU256;
73
74            fn mul(self, rhs: SqlU256) -> Self::Output {
75                SqlU256::from(U256::from(self) * rhs.0)
76            }
77        }
78
79        impl Div<SqlU256> for $prim_type {
80            type Output = SqlU256;
81
82            fn div(self, rhs: SqlU256) -> Self::Output {
83                SqlU256::from(U256::from(self) / rhs.0)
84            }
85        }
86
87        impl Rem<SqlU256> for $prim_type {
88            type Output = SqlU256;
89
90            fn rem(self, rhs: SqlU256) -> Self::Output {
91                SqlU256::from(U256::from(self) % rhs.0)
92            }
93        }
94
95        // Reference variants for SqlU256 op &primitive
96        impl Add<&$prim_type> for SqlU256 {
97            type Output = Self;
98
99            fn add(self, rhs: &$prim_type) -> Self::Output {
100                SqlU256::from(self.0 + U256::from(*rhs))
101            }
102        }
103
104        impl Sub<&$prim_type> for SqlU256 {
105            type Output = Self;
106
107            fn sub(self, rhs: &$prim_type) -> Self::Output {
108                SqlU256::from(self.0 - U256::from(*rhs))
109            }
110        }
111
112        impl Mul<&$prim_type> for SqlU256 {
113            type Output = Self;
114
115            fn mul(self, rhs: &$prim_type) -> Self::Output {
116                SqlU256::from(self.0 * U256::from(*rhs))
117            }
118        }
119
120        impl Div<&$prim_type> for SqlU256 {
121            type Output = Self;
122
123            fn div(self, rhs: &$prim_type) -> Self::Output {
124                SqlU256::from(self.0 / U256::from(*rhs))
125            }
126        }
127
128        impl Rem<&$prim_type> for SqlU256 {
129            type Output = Self;
130
131            fn rem(self, rhs: &$prim_type) -> Self::Output {
132                SqlU256::from(self.0 % U256::from(*rhs))
133            }
134        }
135
136        // Reference variants for &SqlU256 op primitive
137        impl Add<$prim_type> for &SqlU256 {
138            type Output = SqlU256;
139
140            fn add(self, rhs: $prim_type) -> Self::Output {
141                SqlU256::from(self.0 + U256::from(rhs))
142            }
143        }
144
145        impl Sub<$prim_type> for &SqlU256 {
146            type Output = SqlU256;
147
148            fn sub(self, rhs: $prim_type) -> Self::Output {
149                SqlU256::from(self.0 - U256::from(rhs))
150            }
151        }
152
153        impl Mul<$prim_type> for &SqlU256 {
154            type Output = SqlU256;
155
156            fn mul(self, rhs: $prim_type) -> Self::Output {
157                SqlU256::from(self.0 * U256::from(rhs))
158            }
159        }
160
161        impl Div<$prim_type> for &SqlU256 {
162            type Output = SqlU256;
163
164            fn div(self, rhs: $prim_type) -> Self::Output {
165                SqlU256::from(self.0 / U256::from(rhs))
166            }
167        }
168
169        impl Rem<$prim_type> for &SqlU256 {
170            type Output = SqlU256;
171
172            fn rem(self, rhs: $prim_type) -> Self::Output {
173                SqlU256::from(self.0 % U256::from(rhs))
174            }
175        }
176
177        // Reference variants for &SqlU256 op &primitive
178        impl Add<&$prim_type> for &SqlU256 {
179            type Output = SqlU256;
180
181            fn add(self, rhs: &$prim_type) -> Self::Output {
182                SqlU256::from(self.0 + U256::from(*rhs))
183            }
184        }
185
186        impl Sub<&$prim_type> for &SqlU256 {
187            type Output = SqlU256;
188
189            fn sub(self, rhs: &$prim_type) -> Self::Output {
190                SqlU256::from(self.0 - U256::from(*rhs))
191            }
192        }
193
194        impl Mul<&$prim_type> for &SqlU256 {
195            type Output = SqlU256;
196
197            fn mul(self, rhs: &$prim_type) -> Self::Output {
198                SqlU256::from(self.0 * U256::from(*rhs))
199            }
200        }
201
202        impl Div<&$prim_type> for &SqlU256 {
203            type Output = SqlU256;
204
205            fn div(self, rhs: &$prim_type) -> Self::Output {
206                SqlU256::from(self.0 / U256::from(*rhs))
207            }
208        }
209
210        impl Rem<&$prim_type> for &SqlU256 {
211            type Output = SqlU256;
212
213            fn rem(self, rhs: &$prim_type) -> Self::Output {
214                SqlU256::from(self.0 % U256::from(*rhs))
215            }
216        }
217    };
218}
219
220// Implement operations for common integer types
221impl_primitive_ops!(u8);
222impl_primitive_ops!(u16);
223impl_primitive_ops!(u32);
224impl_primitive_ops!(u64);
225impl_primitive_ops!(u128);
226impl_primitive_ops!(usize);
227
228// For signed integers, we only implement the safe operations
229// (addition, multiplication) to avoid potential underflow issues
230macro_rules! impl_signed_ops {
231    ($prim_type:ty) => {
232        // SqlU256 op signed primitive (only safe operations)
233        impl Add<$prim_type> for SqlU256 {
234            type Output = Self;
235
236            fn add(self, rhs: $prim_type) -> Self::Output {
237                if rhs >= 0 {
238                    SqlU256::from(self.0 + U256::from(rhs as u64))
239                } else {
240                    SqlU256::from(self.0 - U256::from((-rhs) as u64))
241                }
242            }
243        }
244
245        impl Sub<$prim_type> for SqlU256 {
246            type Output = Self;
247
248            fn sub(self, rhs: $prim_type) -> Self::Output {
249                if rhs >= 0 {
250                    SqlU256::from(self.0 - U256::from(rhs as u64))
251                } else {
252                    SqlU256::from(self.0 + U256::from((-rhs) as u64))
253                }
254            }
255        }
256
257        impl Mul<$prim_type> for SqlU256 {
258            type Output = Self;
259
260            fn mul(self, rhs: $prim_type) -> Self::Output {
261                if rhs >= 0 {
262                    SqlU256::from(self.0 * U256::from(rhs as u64))
263                } else {
264                    // For negative multiplication, we'd need to handle the sign,
265                    // but since SqlU256 is unsigned, we panic to avoid confusion
266                    panic!("Cannot multiply SqlU256 by negative number")
267                }
268            }
269        }
270
271        impl Div<$prim_type> for SqlU256 {
272            type Output = Self;
273
274            fn div(self, rhs: $prim_type) -> Self::Output {
275                if rhs > 0 {
276                    SqlU256::from(self.0 / U256::from(rhs as u64))
277                } else if rhs == 0 {
278                    panic!("attempt to divide by zero")
279                } else {
280                    panic!("Cannot divide SqlU256 by negative number")
281                }
282            }
283        }
284
285        // signed primitive op SqlU256 (only safe operations)
286        impl Add<SqlU256> for $prim_type {
287            type Output = SqlU256;
288
289            fn add(self, rhs: SqlU256) -> Self::Output {
290                if self >= 0 {
291                    SqlU256::from(U256::from(self as u64) + rhs.0)
292                } else {
293                    if rhs.0 >= U256::from((-self) as u64) {
294                        SqlU256::from(rhs.0 - U256::from((-self) as u64))
295                    } else {
296                        panic!("Cannot subtract larger value from smaller in unsigned context")
297                    }
298                }
299            }
300        }
301
302        impl Mul<SqlU256> for $prim_type {
303            type Output = SqlU256;
304
305            fn mul(self, rhs: SqlU256) -> Self::Output {
306                if self >= 0 {
307                    SqlU256::from(U256::from(self as u64) * rhs.0)
308                } else {
309                    panic!("Cannot multiply negative number by SqlU256")
310                }
311            }
312        }
313    };
314}
315
316impl_signed_ops!(i8);
317impl_signed_ops!(i16);
318impl_signed_ops!(i32);
319impl_signed_ops!(i64);
320impl_signed_ops!(i128);
321impl_signed_ops!(isize);
322
323#[cfg(test)]
324mod tests {
325    use super::*;
326
327    #[test]
328    fn test_unsigned_primitive_operations() {
329        let value = SqlU256::from(100u64);
330
331        // SqlU256 op primitive
332        assert_eq!(value + 50u64, SqlU256::from(150u64));
333        assert_eq!(value - 30u64, SqlU256::from(70u64));
334        assert_eq!(value * 2u64, SqlU256::from(200u64));
335        assert_eq!(value / 2u64, SqlU256::from(50u64));
336        assert_eq!(value % 30u64, SqlU256::from(10u64));
337
338        // primitive op SqlU256
339        assert_eq!(50u64 + value, SqlU256::from(150u64));
340        assert_eq!(200u64 - value, SqlU256::from(100u64));
341        assert_eq!(2u64 * value, SqlU256::from(200u64));
342        assert_eq!(1000u64 / value, SqlU256::from(10u64));
343        assert_eq!(250u64 % value, SqlU256::from(50u64));
344    }
345
346    #[test]
347    fn test_different_unsigned_types() {
348        let value = SqlU256::from(100u64);
349
350        // Test with different unsigned types
351        assert_eq!(value * 2u8, SqlU256::from(200u64));
352        assert_eq!(value * 2u16, SqlU256::from(200u64));
353        assert_eq!(value * 2u32, SqlU256::from(200u64));
354        assert_eq!(value * 2u128, SqlU256::from(200u64));
355        assert_eq!(value * 2usize, SqlU256::from(200u64));
356
357        assert_eq!(2u8 * value, SqlU256::from(200u64));
358        assert_eq!(2u16 * value, SqlU256::from(200u64));
359        assert_eq!(2u32 * value, SqlU256::from(200u64));
360        assert_eq!(2u128 * value, SqlU256::from(200u64));
361        assert_eq!(2usize * value, SqlU256::from(200u64));
362    }
363
364    #[test]
365    fn test_reference_operations() {
366        let value = SqlU256::from(100u64);
367        let multiplier = 2u64;
368
369        // Test reference variants
370        assert_eq!(value * &multiplier, SqlU256::from(200u64));
371        assert_eq!(&value * multiplier, SqlU256::from(200u64));
372        assert_eq!(&value * &multiplier, SqlU256::from(200u64));
373    }
374
375    #[test]
376    fn test_signed_positive_operations() {
377        let value = SqlU256::from(100u64);
378
379        // Positive signed integers should work
380        assert_eq!(value + 50i64, SqlU256::from(150u64));
381        assert_eq!(value * 2i64, SqlU256::from(200u64));
382        assert_eq!(50i64 + value, SqlU256::from(150u64));
383        assert_eq!(2i64 * value, SqlU256::from(200u64));
384    }
385
386    #[test]
387    fn test_signed_negative_addition() {
388        let value = SqlU256::from(100u64);
389
390        // Negative addition (should work like subtraction)
391        assert_eq!(value + (-30i64), SqlU256::from(70u64));
392
393        // Negative + positive SqlU256
394        assert_eq!((-30i64) + value, SqlU256::from(70u64));
395    }
396
397    #[test]
398    #[should_panic(expected = "Cannot multiply SqlU256 by negative number")]
399    fn test_signed_negative_multiplication_panic() {
400        let value = SqlU256::from(100u64);
401        let _ = value * (-2i64);
402    }
403
404    #[test]
405    #[should_panic(expected = "Cannot multiply negative number by SqlU256")]
406    fn test_negative_multiply_sqlu256_panic() {
407        let value = SqlU256::from(100u64);
408        let _ = (-2i64) * value;
409    }
410
411    #[test]
412    #[should_panic(expected = "Cannot subtract larger value from smaller in unsigned context")]
413    fn test_negative_addition_underflow_panic() {
414        let value = SqlU256::from(50u64);
415        let _ = (-100i64) + value; // Would result in negative, should panic
416    }
417
418    #[test]
419    fn test_common_ethereum_scenarios() {
420        // Common Ethereum scenarios
421        let balance = SqlU256::from(1_000_000_000_000_000_000u64); // 1 ETH in wei
422        let gas_price = SqlU256::from(20_000_000_000u64); // 20 Gwei
423
424        // Double the balance
425        let doubled_balance = balance * 2;
426        assert_eq!(doubled_balance, SqlU256::from(2_000_000_000_000_000_000u64));
427
428        // Calculate transaction cost (gas price * gas limit)
429        let gas_limit = 21000u64;
430        let tx_cost = gas_price * gas_limit;
431        assert_eq!(tx_cost, SqlU256::from(420_000_000_000_000u64));
432
433        // Calculate remaining balance
434        let remaining = balance - tx_cost;
435        assert_eq!(remaining, SqlU256::from(999_580_000_000_000_000u64));
436
437        // Percentage calculations (e.g., 5% fee)
438        let fee_percentage = 5u64;
439        let fee = balance * fee_percentage / 100u64;
440        assert_eq!(fee, SqlU256::from(50_000_000_000_000_000u64));
441    }
442}