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#[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}