easy_int/
lib.rs

1#[macro_export]
2macro_rules! implement_trait {
3    ($type:ident, $inner:ty, $int:ty, $trait:ident, $method:ident) => {
4        impl $trait<$int> for $type {
5            type Output = $type;
6            fn $method(self, other: $int) -> Self::Output {
7                Self(self.0.$method(other as $inner))
8            }
9        }
10        impl $trait<$type> for $int {
11            type Output = $type;
12            fn $method(self, other: $type) -> Self::Output {
13                $type((self as $inner).$method(other.0))
14            }
15        }
16        impl $trait for $type {
17            type Output = $type;
18            fn $method(self, other: $type) -> Self::Output {
19                Self(self.0.$method(other.0))
20            }
21        }
22    };
23}
24
25#[macro_export]
26macro_rules! implement_eq_trait {
27    ($type:ident, $inner:ty, $int:ty) => {
28        impl PartialEq<$int> for $type {
29            fn eq(&self, other: &$int) -> bool {
30                self.0 == *other as $inner
31            }
32        }
33        impl PartialEq<$type> for $int {
34            fn eq(&self, other: &$type) -> bool {
35                (*self as $inner) == other.0
36            }
37        }
38    };
39}
40
41#[macro_export]
42macro_rules! implement_ord_trait {
43    ($type:ident, $inner:ty, $int:ty) => {
44        impl PartialOrd<$int> for $type {
45            fn partial_cmp(&self, other: &$int) -> Option<std::cmp::Ordering> {
46                self.0.partial_cmp(&(*other as $inner))
47            }
48        }
49        impl PartialOrd<$type> for $int {
50            fn partial_cmp(&self, other: &$type) -> Option<std::cmp::Ordering> {
51                (*self as $inner).partial_cmp(&other.0)
52            }
53        }
54    };
55}
56
57#[macro_export]
58macro_rules! implement_from {
59    ($type:ident, $inner:ty, $($int:ty),*) => {
60        $(impl From<$int> for $type {
61            fn from(value: $int) -> Self {
62                Self(value as $inner)
63            }
64        })*
65    };
66}
67
68#[macro_export]
69macro_rules! implement_special_methods {
70    ($type:ident, $inner:ty) => {
71        impl $type {
72            pub fn saturating_add(self, rhs: impl Into<$type>) -> Self {
73                let rhs: $inner = rhs.into().0;
74                Self(self.0.saturating_add(rhs))
75            }
76            pub fn saturating_sub(self, rhs: impl Into<$type>) -> Self {
77                let rhs: $inner = rhs.into().0;
78                Self(self.0.saturating_sub(rhs))
79            }
80            pub fn wrapping_add(self, rhs: impl Into<$type>) -> Self {
81                let rhs: $inner = rhs.into().0;
82                Self(self.0.wrapping_add(rhs))
83            }
84            pub fn wrapping_sub(self, rhs: impl Into<$type>) -> Self {
85                let rhs: $inner = rhs.into().0;
86                Self(self.0.wrapping_sub(rhs))
87            }
88            pub fn checked_add(self, rhs: impl Into<$type>) -> Option<Self> {
89                let rhs: $inner = rhs.into().0;
90                self.0.checked_add(rhs).map(Self)
91            }
92            pub fn checked_sub(self, rhs: impl Into<$type>) -> Option<Self> {
93                let rhs: $inner = rhs.into().0;
94                self.0.checked_sub(rhs).map(Self)
95            }
96            pub fn overflowing_add(self, rhs: impl Into<$type>) -> (Self, bool) {
97                let rhs: $inner = rhs.into().0;
98                let (val, overflow) = self.0.overflowing_add(rhs);
99                (Self(val), overflow)
100            }
101            pub fn overflowing_sub(self, rhs: impl Into<$type>) -> (Self, bool) {
102                let rhs: $inner = rhs.into().0;
103                let (val, overflow) = self.0.overflowing_sub(rhs);
104                (Self(val), overflow)
105            }
106        }
107    };
108}
109
110/// A macros for implementing integer types.
111/// When you defining a wrapper over ineger type you can
112/// Use this macros and all basic operations like conversions,
113/// arithmetic, will be implemented for you:
114/// ```
115/// struct MyIntWrapper(u64);
116/// implement_int!(MyIntWrapper, u64);
117/// ```
118#[macro_export]
119macro_rules! implement_int {
120    ($type:ident, $inner:ty) => {
121        $crate::implement_from!($type, $inner, u8, u16, u32, u64, i8, i16, i32, i64, usize);
122
123        impl From<$type> for $inner {
124            fn from(value: $type) -> Self {
125                value.0
126            }
127        }
128
129        impl std::fmt::Display for $type {
130            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131                write!(f, "{}", self.0)
132            }
133        }
134
135        $crate::implement_trait!($type, $inner, $inner, Add, add);
136        $crate::implement_trait!($type, $inner, $inner, Sub, sub);
137        $crate::implement_trait!($type, $inner, $inner, Mul, mul);
138        $crate::implement_trait!($type, $inner, $inner, Div, div);
139        $crate::implement_trait!($type, $inner, $inner, Rem, rem);
140        $crate::implement_eq_trait!($type, $inner, $inner);
141        $crate::implement_ord_trait!($type, $inner, $inner);
142
143        $crate::implement_special_methods!($type, $inner);
144    };
145}
146
147#[cfg(test)]
148mod tests {
149    use super::*;
150    use std::cmp::{PartialEq, PartialOrd};
151    use std::convert::From;
152    use std::ops::{Add, Div, Mul, Rem, Sub};
153
154    #[derive(Debug, Clone, Copy)]
155    struct TestIntU64(u64);
156    #[derive(Debug, Clone, Copy)]
157    struct TestIntU32(u32);
158
159    #[derive(Debug, Clone, Copy)]
160    struct TetIntI32(i32);
161
162    implement_int!(TetIntI32, i32);
163    implement_int!(TestIntU64, u64);
164    implement_int!(TestIntU32, u32);
165
166    #[test]
167    fn test_basic_add() {
168        let a = TestIntU64(3);
169        let b = 4;
170        let c = a + b;
171        assert_eq!(c, 7);
172    }
173
174    #[test]
175    #[should_panic]
176    fn test_add_overflow() {
177        let a = TestIntU32(u32::max_value());
178        let b: u32 = 1;
179        let _ = a + b;
180    }
181
182    #[test]
183    fn test_conversion_from_different_type() {
184        let _: TestIntU64 = 10u8.into();
185    }
186
187    #[test]
188    fn all_side_add_works() {
189        let a = TestIntU32(13);
190        let b = 1u32;
191        let _ = a + b;
192        let _ = b + a;
193        let a = TestIntU32(3);
194        let b = TestIntU32(1);
195        let _ = a + b;
196    }
197
198    #[test]
199    fn all_side_cmp_works() {
200        let a = TestIntU32(13);
201        let b = 1u32;
202        let _ = a < b;
203        let _ = b < a;
204        let _ = a > b;
205        let _ = b > a;
206        let _ = b == a;
207        let _ = a == b;
208        let _ = a >= b;
209        let _ = b >= a;
210        let _ = b != a;
211        let _ = a != b;
212    }
213
214    #[test]
215    fn print_works() {
216        let a = TetIntI32(-3);
217        let formatted = format!("{}", a);
218        assert_eq!(formatted, "-3");
219    }
220
221    #[test]
222    fn test_conversion() {
223        let x: TestIntU32 = 10u32.into();
224        let y: u32 = x.into();
225        assert_eq!(y, 10);
226    }
227
228    #[test]
229    fn test_saturating_sub() {
230        let x = TestIntU32(5);
231        assert_eq!(x.saturating_sub(10).0, 0);
232    }
233
234    #[test]
235    fn test_wrapping_add() {
236        let x = TestIntU32(u32::MAX);
237        assert_eq!(x.wrapping_add(1).0, 0);
238    }
239
240    #[test]
241    fn test_checked_add() {
242        let x = TestIntU32(u32::MAX);
243        assert!(x.checked_add(1).is_none());
244    }
245
246    #[test]
247    fn test_overflowing_sub() {
248        let x = TestIntU32(0);
249        let (result, overflow) = x.overflowing_sub(1);
250        assert_eq!(result.0, u32::MAX);
251        assert!(overflow);
252    }
253}