Skip to main content

reifydb_value/value/number/safe/convert/
int.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2026 ReifyDB
3
4use bigdecimal::BigDecimal as BigDecimalInner;
5
6use super::*;
7
8macro_rules! impl_safe_convert_int_to_signed {
9    ($($dst:ty),*) => {
10        $(
11            impl SafeConvert<$dst> for Int {
12                fn checked_convert(self) -> Option<$dst> {
13                    <$dst>::try_from(&self.0).ok()
14                }
15
16                fn saturating_convert(self) -> $dst {
17                    if let Ok(val) = <$dst>::try_from(&self.0) {
18                        val
19                    } else if self.0 < BigInt::from(0) {
20                        <$dst>::MIN
21                    } else {
22                        <$dst>::MAX
23                    }
24                }
25
26                fn wrapping_convert(self) -> $dst {
27                    if let Some(val) = self.0.to_i64() {
28                        val as $dst
29                    } else if let Some(val) = self.0.to_i128() {
30                        val as $dst
31                    } else {
32
33                        self.saturating_convert()
34                    }
35                }
36            }
37        )*
38    };
39}
40
41macro_rules! impl_safe_convert_int_to_unsigned {
42    ($($dst:ty),*) => {
43        $(
44            impl SafeConvert<$dst> for Int {
45                fn checked_convert(self) -> Option<$dst> {
46                    if self.0 >= BigInt::from(0) {
47                        <$dst>::try_from(&self.0).ok()
48                    } else {
49                        None
50                    }
51                }
52
53                fn saturating_convert(self) -> $dst {
54                    if self.0 < BigInt::from(0) {
55                        0
56                    } else if let Ok(val) = <$dst>::try_from(&self.0) {
57                        val
58                    } else {
59                        <$dst>::MAX
60                    }
61                }
62
63                fn wrapping_convert(self) -> $dst {
64                    if self.0 < BigInt::from(0) {
65                        0
66                    } else if let Ok(val) = <$dst>::try_from(&self.0) {
67                        val
68                    } else {
69                        self.saturating_convert()
70                    }
71                }
72            }
73        )*
74    };
75}
76
77macro_rules! impl_safe_convert_int_to_float {
78    ($($dst:ty),*) => {
79        $(
80            impl SafeConvert<$dst> for Int {
81                fn checked_convert(self) -> Option<$dst> {
82                    self.0.to_f64().and_then(|f| {
83                        if f.is_finite() {
84                            Some(f as $dst)
85                        } else {
86                            None
87                        }
88                    })
89                }
90
91                fn saturating_convert(self) -> $dst {
92                    if let Some(f) = self.0.to_f64() {
93                        if f.is_finite() {
94                            f as $dst
95                        } else if f.is_sign_negative() {
96                            <$dst>::MIN
97                        } else {
98                            <$dst>::MAX
99                        }
100                    } else if self.0 < BigInt::from(0) {
101                        <$dst>::MIN
102                    } else {
103                        <$dst>::MAX
104                    }
105                }
106
107                fn wrapping_convert(self) -> $dst {
108                    self.saturating_convert()
109                }
110            }
111        )*
112    };
113}
114
115impl_safe_convert_int_to_signed!(i8, i16, i32, i64, i128);
116impl_safe_convert_int_to_unsigned!(u8, u16, u32, u64, u128);
117impl_safe_convert_int_to_float!(f32, f64);
118
119impl SafeConvert<Uint> for Int {
120	fn checked_convert(self) -> Option<Uint> {
121		if self.0 >= BigInt::from(0) {
122			Some(Uint(self.0))
123		} else {
124			None
125		}
126	}
127
128	fn saturating_convert(self) -> Uint {
129		if self.0 >= BigInt::from(0) {
130			Uint(self.0)
131		} else {
132			Uint::zero()
133		}
134	}
135
136	fn wrapping_convert(self) -> Uint {
137		Uint(self.0.abs())
138	}
139}
140
141impl SafeConvert<Decimal> for Int {
142	fn checked_convert(self) -> Option<Decimal> {
143		let big_decimal = BigDecimalInner::from(self.0);
144		Some(Decimal::from(big_decimal))
145	}
146
147	fn saturating_convert(self) -> Decimal {
148		let big_decimal = BigDecimalInner::from(self.0);
149		Decimal::from(big_decimal)
150	}
151
152	fn wrapping_convert(self) -> Decimal {
153		self.saturating_convert()
154	}
155}
156
157#[cfg(test)]
158pub mod tests {
159	use super::*;
160
161	mod i8 {
162		use super::*;
163
164		#[test]
165		fn test_checked_convert() {
166			let x = Int::from(-128);
167			let y: Option<i8> = x.checked_convert();
168			assert_eq!(y, Some(-128i8));
169		}
170
171		#[test]
172		fn test_checked_convert_overflow() {
173			let x = Int::from(128);
174			let y: Option<i8> = x.checked_convert();
175			assert_eq!(y, None);
176		}
177
178		#[test]
179		fn test_saturating_convert() {
180			let x = Int::from(200);
181			let y: i8 = x.saturating_convert();
182			assert_eq!(y, i8::MAX);
183		}
184
185		#[test]
186		fn test_wrapping_convert() {
187			let x = Int::from(-129);
188			let y: i8 = x.wrapping_convert();
189			assert_eq!(y, i8::MAX);
190		}
191	}
192
193	mod i32 {
194		use super::*;
195
196		#[test]
197		fn test_checked_convert() {
198			let x = Int::from(-2147483648i64);
199			let y: Option<i32> = x.checked_convert();
200			assert_eq!(y, Some(-2147483648i32));
201		}
202
203		#[test]
204		fn test_saturating_convert() {
205			let x = Int::from(2147483648i64);
206			let y: i32 = x.saturating_convert();
207			assert_eq!(y, i32::MAX);
208		}
209	}
210
211	mod u8 {
212		use super::*;
213
214		#[test]
215		fn test_checked_convert_positive() {
216			let x = Int::from(42);
217			let y: Option<u8> = x.checked_convert();
218			assert_eq!(y, Some(42u8));
219		}
220
221		#[test]
222		fn test_checked_convert_negative() {
223			let x = Int::from(-1);
224			let y: Option<u8> = x.checked_convert();
225			assert_eq!(y, None);
226		}
227
228		#[test]
229		fn test_saturating_convert() {
230			let x = Int::from(-10);
231			let y: u8 = x.saturating_convert();
232			assert_eq!(y, 0u8);
233		}
234	}
235
236	mod u32 {
237		use super::*;
238
239		#[test]
240		fn test_checked_convert() {
241			let x = Int::from(4294967295u64);
242			let y: Option<u32> = x.checked_convert();
243			assert_eq!(y, Some(4294967295u32));
244		}
245
246		#[test]
247		fn test_saturating_convert() {
248			let x = Int::from(4294967296u64);
249			let y: u32 = x.saturating_convert();
250			assert_eq!(y, u32::MAX);
251		}
252	}
253
254	mod f32 {
255		use super::*;
256
257		#[test]
258		fn test_checked_convert() {
259			let x = Int::from(42);
260			let y: Option<f32> = x.checked_convert();
261			assert_eq!(y, Some(42.0f32));
262		}
263
264		#[test]
265		fn test_saturating_convert() {
266			let x = Int::from(-1000);
267			let y: f32 = x.saturating_convert();
268			assert_eq!(y, -1000.0f32);
269		}
270	}
271
272	mod f64 {
273		use super::*;
274
275		#[test]
276		fn test_checked_convert() {
277			let x = Int::from(42);
278			let y: Option<f64> = x.checked_convert();
279			assert_eq!(y, Some(42.0f64));
280		}
281
282		#[test]
283		fn test_saturating_convert() {
284			let x = Int::from(-1000);
285			let y: f64 = x.saturating_convert();
286			assert_eq!(y, -1000.0f64);
287		}
288	}
289
290	mod uint {
291		use super::*;
292
293		#[test]
294		fn test_checked_convert_positive() {
295			let x = Int::from(42);
296			let y: Option<Uint> = x.checked_convert();
297			assert!(y.is_some());
298			assert_eq!(y.unwrap().to_string(), "42");
299		}
300
301		#[test]
302		fn test_checked_convert_negative() {
303			let x = Int::from(-1);
304			let y: Option<Uint> = x.checked_convert();
305			assert!(y.is_none());
306		}
307
308		#[test]
309		fn test_saturating_convert() {
310			let x = Int::from(-100);
311			let y: Uint = x.saturating_convert();
312			assert_eq!(y.to_string(), "0");
313		}
314
315		#[test]
316		fn test_wrapping_convert() {
317			let x = Int::from(-1);
318			let y: Uint = x.wrapping_convert();
319			assert_eq!(y.to_string(), "1");
320		}
321	}
322
323	mod decimal {
324		use super::*;
325
326		#[test]
327		fn test_checked_convert() {
328			let x = Int::from(12345);
329			let y: Option<Decimal> = x.checked_convert();
330			assert!(y.is_some());
331			let decimal = y.unwrap();
332			assert_eq!(decimal.to_string(), "12345");
333		}
334
335		#[test]
336		fn test_checked_convert_zero() {
337			let x = Int::from(0);
338			let y: Option<Decimal> = x.checked_convert();
339			assert!(y.is_some());
340			let decimal = y.unwrap();
341			assert_eq!(decimal.to_string(), "0");
342		}
343
344		#[test]
345		fn test_checked_convert_large() {
346			// Test with a very large number
347			let x = Int::from(i128::MAX);
348			let y: Option<Decimal> = x.checked_convert();
349			assert!(y.is_some());
350			let decimal = y.unwrap();
351			assert_eq!(decimal.to_string(), "170141183460469231731687303715884105727");
352		}
353
354		#[test]
355		fn test_saturating_convert() {
356			let x = Int::from(-999999);
357			let y: Decimal = x.saturating_convert();
358			assert_eq!(y.to_string(), "-999999");
359		}
360
361		#[test]
362		fn test_wrapping_convert() {
363			let x = Int::from(42);
364			let y: Decimal = x.wrapping_convert();
365			assert_eq!(y.to_string(), "42");
366		}
367	}
368
369	mod self_conversion {
370		use super::*;
371
372		#[test]
373		fn test_checked_convert() {
374			let x = Int::from(42);
375			let y: Option<Int> = x.clone().checked_convert();
376			assert_eq!(y, Some(x));
377		}
378
379		#[test]
380		fn test_saturating_convert() {
381			let x = Int::from(-100);
382			let y: Int = x.clone().saturating_convert();
383			assert_eq!(y, x);
384		}
385
386		#[test]
387		fn test_wrapping_convert() {
388			let x = Int::from(999);
389			let y: Int = x.clone().wrapping_convert();
390			assert_eq!(y, x);
391		}
392	}
393}