Skip to main content

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

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4pub trait SafeConvert<T>: Sized {
5	fn checked_convert(self) -> Option<T>;
6	fn saturating_convert(self) -> T;
7	fn wrapping_convert(self) -> T;
8}
9
10macro_rules! impl_safe_convert {
11    ($src:ty => $($dst:ty),*) => {
12        $(
13            impl SafeConvert<$dst> for $src {
14                fn checked_convert(self) -> Option<$dst> {
15                    <$dst>::try_from(self).ok()
16                }
17
18                fn saturating_convert(self) -> $dst {
19                    if let Ok(v) = <$dst>::try_from(self) {
20                        v
21                    } else if self < 0 {
22                        0
23                    } else {
24                        <$dst>::MAX
25                    }
26                }
27
28                fn wrapping_convert(self) -> $dst {
29                    self as $dst
30                }
31            }
32        )*
33    };
34}
35
36macro_rules! impl_safe_unsigned_convert {
37    ($src:ty => $($dst:ty),*) => {
38        $(
39            impl SafeConvert<$dst> for $src {
40                fn checked_convert(self) -> Option<$dst> {
41                    <$dst>::try_from(self).ok()
42                }
43
44                fn saturating_convert(self) -> $dst {
45                    if self > <$dst>::MAX as $src {
46                        <$dst>::MAX
47                    }else{
48                        self as $dst
49                    }
50                }
51
52                fn wrapping_convert(self) -> $dst {
53                    self as $dst
54                }
55            }
56        )*
57    };
58}
59
60macro_rules! impl_safe_convert_signed_to_float {
61    ($mantissa_bits:expr; $src:ty => $($float:ty),* $(,)?) => {
62        $(
63            impl SafeConvert<$float> for $src {
64                fn checked_convert(self) -> Option<$float> {
65                    let val = self as i128;
66                    let max_exact = 1i128 << $mantissa_bits;
67                    if val >= -max_exact && val <= max_exact {
68                        Some(self as $float)
69                    } else {
70                        None
71                    }
72                }
73
74                fn saturating_convert(self) -> $float {
75                    let max_exact = 1i128 << $mantissa_bits;
76                    let min = -max_exact;
77                    let max = max_exact;
78                    let val = self as i128;
79                    if val < min {
80                        min as $float
81                    } else if val > max {
82                        max as $float
83                    } else {
84                        self as $float
85                    }
86                }
87
88                fn wrapping_convert(self) -> $float {
89                    self as $float
90                }
91            }
92        )*
93    };
94}
95
96macro_rules! impl_safe_convert_unsigned_to_float {
97    ($mantissa_bits:expr; $src:ty => $($float:ty),* $(,)?) => {
98        $(
99            impl SafeConvert<$float> for $src {
100                fn checked_convert(self) -> Option<$float> {
101                    if self as u64 <= (1u64 << $mantissa_bits) {
102                        Some(self as $float)
103                    } else {
104                        None
105                    }
106                }
107
108                fn saturating_convert(self) -> $float {
109                    let max_exact = 1u64 << $mantissa_bits;
110                    let max = max_exact as u128;
111                    let val = self as u128;
112                    if val > max {
113                           max as $float
114                    } else {
115                        self as $float
116                    }
117                }
118
119                fn wrapping_convert(self) -> $float {
120                    self as $float
121                }
122            }
123        )*
124    };
125}
126
127macro_rules! impl_safe_convert_float_to_signed {
128    ($src:ty => $($dst:ty),* $(,)?) => {
129        $(
130            impl SafeConvert<$dst> for $src {
131                fn checked_convert(self) -> Option<$dst> {
132                    if self.is_nan() || self.is_infinite() {
133                        return None;
134                    }
135
136                    let min_val = <$dst>::MIN as $src;
137                    let max_val = <$dst>::MAX as $src;
138
139                    if self < min_val || self > max_val {
140                        None
141                    } else {
142                        Some(self as $dst)
143                    }
144                }
145
146                fn saturating_convert(self) -> $dst {
147                    if self.is_nan() {
148                        return 0;
149                    }
150
151                    if self.is_infinite() {
152                        return if self.is_sign_positive() { <$dst>::MAX } else { <$dst>::MIN };
153                    }
154
155                    let min_val = <$dst>::MIN as $src;
156                    let max_val = <$dst>::MAX as $src;
157
158                    if self < min_val {
159                        <$dst>::MIN
160                    } else if self > max_val {
161                        <$dst>::MAX
162                    } else {
163                        self as $dst
164                    }
165                }
166
167                fn wrapping_convert(self) -> $dst {
168                    if self.is_nan() {
169                        return 0;
170                    }
171
172                    if self.is_infinite() {
173                        return if self.is_sign_positive() { <$dst>::MAX } else { <$dst>::MIN };
174                    }
175
176                    self as $dst
177                }
178            }
179        )*
180    };
181}
182
183macro_rules! impl_safe_convert_float_to_unsigned {
184    ($src:ty => $($dst:ty),* $(,)?) => {
185        $(
186            impl SafeConvert<$dst> for $src {
187                fn checked_convert(self) -> Option<$dst> {
188                    if self.is_nan() || self.is_infinite() || self < 0.0 {
189                        return None;
190                    }
191
192                    let max_val = <$dst>::MAX as $src;
193
194                    if self > max_val {
195                        None
196                    } else {
197                        Some(self as $dst)
198                    }
199                }
200
201                fn saturating_convert(self) -> $dst {
202                    if self.is_nan() || self < 0.0 {
203                        return 0;
204                    }
205
206                    if self.is_infinite() {
207                        return <$dst>::MAX;
208                    }
209
210                    let max_val = <$dst>::MAX as $src;
211
212                    if self > max_val {
213                        <$dst>::MAX
214                    } else {
215                        self as $dst
216                    }
217                }
218
219                fn wrapping_convert(self) -> $dst {
220                    if self.is_nan() || self < 0.0 {
221                        return 0;
222                    }
223
224                    if self.is_infinite() {
225                        return <$dst>::MAX;
226                    }
227
228                    self as $dst
229                }
230            }
231        )*
232    };
233}
234
235use arrow_buffer::i256;
236
237use crate::value::decimal::Decimal;
238
239fn narrow_i256<T: TryFrom<i128> + TryFrom<u128>>(value: i256) -> Option<T> {
240	match value.to_i128() {
241		Some(value) => T::try_from(value).ok(),
242		None => match value.to_parts() {
243			(low, 0) => T::try_from(low).ok(),
244			_ => None,
245		},
246	}
247}
248
249macro_rules! impl_safe_convert_promote {
250    ($src:ty => $($dst:ty),* $(,)?) => {
251        $(
252            impl SafeConvert<$dst> for $src {
253                fn checked_convert(self) -> Option<$dst> {
254                   Some(self as $dst)
255                }
256
257                fn saturating_convert(self) -> $dst {
258                    self as $dst
259                }
260
261                fn wrapping_convert(self) -> $dst {
262                    self as $dst
263                }
264            }
265        )*
266    };
267}
268
269macro_rules! impl_safe_convert_demote {
270    ($src:ty => $($dst:ty),* $(,)?) => {
271        $(
272            impl SafeConvert<$dst> for $src {
273                fn checked_convert(self) -> Option<$dst> {
274                    <$dst>::try_from(self).ok()
275                }
276
277                fn saturating_convert(self) -> $dst {
278                    match <$dst>::try_from(self) {
279                        Ok(v) => v,
280                        Err(_) => {
281                            if self < <$dst>::MIN as $src {
282                                <$dst>::MIN
283                            } else {
284                                <$dst>::MAX
285                            }
286                        }
287                    }
288                }
289
290                fn wrapping_convert(self) -> $dst {
291                    self as $dst
292                }
293            }
294        )*
295    };
296}
297
298macro_rules! impl_safe_convert_unsigned_demote {
299    ($src:ty => $($dst:ty),* $(,)?) => {
300        $(
301            impl SafeConvert<$dst> for $src {
302                fn checked_convert(self) -> Option<$dst> {
303                    <$dst>::try_from(self).ok()
304                }
305
306                fn saturating_convert(self) -> $dst {
307                    if self > <$dst>::MAX as $src {
308                        <$dst>::MAX
309                    } else {
310                        self as $dst
311                    }
312                }
313
314                fn wrapping_convert(self) -> $dst {
315                    self as $dst
316                }
317            }
318        )*
319    };
320}
321
322macro_rules! impl_safe_convert_float_demote {
323	($src:ty => $dst:ty) => {
324		impl SafeConvert<$dst> for $src {
325			fn checked_convert(self) -> Option<$dst> {
326				let demoted = self as $dst;
327				if self.is_finite() && demoted.is_finite() {
328					Some(demoted)
329				} else {
330					None
331				}
332			}
333
334			fn saturating_convert(self) -> $dst {
335				if self.is_nan() {
336					<$dst>::NAN
337				} else if self <= <$dst>::MIN as $src {
338					<$dst>::MIN
339				} else if self >= <$dst>::MAX as $src {
340					<$dst>::MAX
341				} else {
342					self as $dst
343				}
344			}
345
346			fn wrapping_convert(self) -> $dst {
347				self as $dst
348			}
349		}
350	};
351}
352
353macro_rules! impl_safe_convert_self {
354    ($($ty:ty),* $(,)?) => {
355        $(
356            impl SafeConvert<$ty> for $ty {
357                fn checked_convert(self) -> Option<$ty> {
358                    Some(self)
359                }
360
361                fn saturating_convert(self) -> $ty {
362                    self
363                }
364
365                fn wrapping_convert(self) -> $ty {
366                    self
367                }
368            }
369        )*
370    };
371}
372
373macro_rules! impl_safe_convert_to_decimal_from_int {
374    ($($src:ty),* $(,)?) => {
375        $(
376            impl SafeConvert<Decimal> for $src {
377                fn checked_convert(self) -> Option<Decimal> {
378                    Some(Decimal::from(self))
379                }
380
381                fn saturating_convert(self) -> Decimal {
382                    Decimal::from(self)
383                }
384
385                fn wrapping_convert(self) -> Decimal {
386                    Decimal::from(self)
387                }
388            }
389        )*
390    };
391}
392
393macro_rules! impl_safe_convert_to_decimal_from_large_int {
394    ($($src:ty),* $(,)?) => {
395        $(
396            impl SafeConvert<Decimal> for $src {
397                fn checked_convert(self) -> Option<Decimal> {
398                    Some(Decimal::from(self))
399                }
400
401                fn saturating_convert(self) -> Decimal {
402                    Decimal::from(self)
403                }
404
405                fn wrapping_convert(self) -> Decimal {
406                    Decimal::from(self)
407                }
408            }
409        )*
410    };
411}
412
413macro_rules! impl_safe_convert_to_decimal_from_uint {
414    ($($src:ty),* $(,)?) => {
415        $(
416            impl SafeConvert<Decimal> for $src {
417                fn checked_convert(self) -> Option<Decimal> {
418                    Some(Decimal::from(self))
419                }
420
421                fn saturating_convert(self) -> Decimal {
422                    Decimal::from(self)
423                }
424
425                fn wrapping_convert(self) -> Decimal {
426                    Decimal::from(self)
427                }
428            }
429        )*
430    };
431}
432
433macro_rules! impl_safe_convert_to_decimal_from_float {
434    ($($src:ty => $from:ident),* $(,)?) => {
435        $(
436            impl SafeConvert<Decimal> for $src {
437                fn checked_convert(self) -> Option<Decimal> {
438                    Decimal::$from(self)
439                }
440
441                fn saturating_convert(self) -> Decimal {
442                    Decimal::from(self)
443                }
444
445                fn wrapping_convert(self) -> Decimal {
446                    Decimal::from(self)
447                }
448            }
449        )*
450    };
451}
452
453impl_safe_convert_self!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
454impl_safe_convert_self!(Decimal);
455
456pub mod decimal;
457pub mod f32;
458pub mod f64;
459pub mod i128;
460pub mod i16;
461pub mod i32;
462pub mod i64;
463pub mod i8;
464pub mod u128;
465pub mod u16;
466pub mod u32;
467pub mod u64;
468pub mod u8;