Skip to main content

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

1// SPDX-License-Identifier: MIT
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 num_bigint::{BigInt, ToBigInt};
236use num_traits::{Signed, ToPrimitive};
237
238use crate::value::{decimal::Decimal, int::Int, uint::Uint};
239
240macro_rules! impl_safe_convert_to_int {
241    ($($from:ty),*) => {
242        $(
243            impl SafeConvert<Int> for $from {
244                fn checked_convert(self) -> Option<Int> {
245                    Some(Int(BigInt::from(self)))
246                }
247
248                fn saturating_convert(self) -> Int {
249                    Int(BigInt::from(self))
250                }
251
252                fn wrapping_convert(self) -> Int {
253                    Int(BigInt::from(self))
254                }
255            }
256        )*
257    };
258}
259
260macro_rules! impl_safe_convert_unsigned_to_uint {
261    ($($from:ty),*) => {
262        $(
263            impl SafeConvert<Uint> for $from {
264                fn checked_convert(self) -> Option<Uint> {
265                    Some(Uint(BigInt::from(self)))
266                }
267
268                fn saturating_convert(self) -> Uint {
269                    Uint(BigInt::from(self))
270                }
271
272                fn wrapping_convert(self) -> Uint {
273                    Uint(BigInt::from(self))
274                }
275            }
276        )*
277    };
278}
279
280macro_rules! impl_safe_convert_float_to_int {
281    ($($from:ty),*) => {
282        $(
283            impl SafeConvert<Int> for $from {
284                fn checked_convert(self) -> Option<Int> {
285                    if self.is_finite() {
286                        let truncated = self.trunc();
287
288                        truncated.to_bigint().map(Int)
289                    } else {
290                        None
291                    }
292                }
293
294                fn saturating_convert(self) -> Int {
295                    if self.is_nan() {
296                        Int::zero()
297                    } else if self.is_infinite() {
298                        if self.is_sign_positive() {
299                            Int(BigInt::from(i64::MAX))
300                        } else {
301                            Int(BigInt::from(i64::MIN))
302                        }
303                    } else {
304                        let truncated = self.trunc() as i64;
305                        Int(BigInt::from(truncated))
306                    }
307                }
308
309                fn wrapping_convert(self) -> Int {
310                    if self.is_finite() {
311                        Int(BigInt::from(self.trunc() as i64))
312                    } else {
313                        Int::zero()
314                    }
315                }
316            }
317        )*
318    };
319}
320
321macro_rules! impl_safe_convert_float_to_uint {
322    ($($from:ty),*) => {
323        $(
324            impl SafeConvert<Uint> for $from {
325                fn checked_convert(self) -> Option<Uint> {
326                    if self.is_finite() && self >= 0.0 {
327                        let truncated = self.trunc();
328
329                        truncated.to_bigint().and_then(|big_int| {
330                            if big_int >= BigInt::from(0) {
331                                Some(Uint(big_int))
332                            } else {
333                                None
334                            }
335                        })
336                    } else {
337                        None
338                    }
339                }
340
341                fn saturating_convert(self) -> Uint {
342                    if self.is_nan() || self < 0.0 {
343                        Uint::zero()
344                    } else if self.is_infinite() {
345                        Uint(BigInt::from(u64::MAX))
346                    } else {
347                        let truncated = self.trunc() as u64;
348                        Uint(BigInt::from(truncated))
349                    }
350                }
351
352                fn wrapping_convert(self) -> Uint {
353                    if self.is_finite() && self >= 0.0 {
354                        Uint(BigInt::from(self.trunc() as u64))
355                    } else if self.is_finite() && self < 0.0 {
356
357                        Uint(BigInt::from(self.trunc() as i64 as u64))
358                    } else {
359                        Uint::zero()
360                    }
361                }
362            }
363        )*
364    };
365}
366
367macro_rules! impl_safe_convert_promote {
368    ($src:ty => $($dst:ty),* $(,)?) => {
369        $(
370            impl SafeConvert<$dst> for $src {
371                fn checked_convert(self) -> Option<$dst> {
372                   Some(self as $dst)
373                }
374
375                fn saturating_convert(self) -> $dst {
376                    self as $dst
377                }
378
379                fn wrapping_convert(self) -> $dst {
380                    self as $dst
381                }
382            }
383        )*
384    };
385}
386
387macro_rules! impl_safe_convert_demote {
388    ($src:ty => $($dst:ty),* $(,)?) => {
389        $(
390            impl SafeConvert<$dst> for $src {
391                fn checked_convert(self) -> Option<$dst> {
392                    <$dst>::try_from(self).ok()
393                }
394
395                fn saturating_convert(self) -> $dst {
396                    match <$dst>::try_from(self) {
397                        Ok(v) => v,
398                        Err(_) => {
399                            if self < <$dst>::MIN as $src {
400                                <$dst>::MIN
401                            } else {
402                                <$dst>::MAX
403                            }
404                        }
405                    }
406                }
407
408                fn wrapping_convert(self) -> $dst {
409                    self as $dst
410                }
411            }
412        )*
413    };
414}
415
416macro_rules! impl_safe_convert_unsigned_demote {
417    ($src:ty => $($dst:ty),* $(,)?) => {
418        $(
419            impl SafeConvert<$dst> for $src {
420                fn checked_convert(self) -> Option<$dst> {
421                    <$dst>::try_from(self).ok()
422                }
423
424                fn saturating_convert(self) -> $dst {
425                    if self > <$dst>::MAX as $src {
426                        <$dst>::MAX
427                    } else {
428                        self as $dst
429                    }
430                }
431
432                fn wrapping_convert(self) -> $dst {
433                    self as $dst
434                }
435            }
436        )*
437    };
438}
439
440macro_rules! impl_safe_convert_float_demote {
441	($src:ty => $dst:ty) => {
442		impl SafeConvert<$dst> for $src {
443			fn checked_convert(self) -> Option<$dst> {
444				let demoted = self as $dst;
445				if self.is_finite() && self >= <$dst>::MIN as $src && self <= <$dst>::MAX as $src {
446					Some(demoted)
447				} else {
448					None
449				}
450			}
451
452			fn saturating_convert(self) -> $dst {
453				if self.is_nan() {
454					<$dst>::NAN
455				} else if self <= <$dst>::MIN as $src {
456					<$dst>::MIN
457				} else if self >= <$dst>::MAX as $src {
458					<$dst>::MAX
459				} else {
460					self as $dst
461				}
462			}
463
464			fn wrapping_convert(self) -> $dst {
465				self as $dst
466			}
467		}
468	};
469}
470
471macro_rules! impl_safe_convert_self {
472    ($($ty:ty),* $(,)?) => {
473        $(
474            impl SafeConvert<$ty> for $ty {
475                fn checked_convert(self) -> Option<$ty> {
476                    Some(self)
477                }
478
479                fn saturating_convert(self) -> $ty {
480                    self
481                }
482
483                fn wrapping_convert(self) -> $ty {
484                    self
485                }
486            }
487        )*
488    };
489}
490
491macro_rules! impl_safe_convert_to_decimal_from_int {
492    ($($src:ty),* $(,)?) => {
493        $(
494            impl SafeConvert<Decimal> for $src {
495                fn checked_convert(self) -> Option<Decimal> {
496                    Some(Decimal::from(self))
497                }
498
499                fn saturating_convert(self) -> Decimal {
500                    Decimal::from(self)
501                }
502
503                fn wrapping_convert(self) -> Decimal {
504                    Decimal::from(self)
505                }
506            }
507        )*
508    };
509}
510
511macro_rules! impl_safe_convert_to_decimal_from_large_int {
512    ($($src:ty),* $(,)?) => {
513        $(
514            impl SafeConvert<Decimal> for $src {
515                fn checked_convert(self) -> Option<Decimal> {
516                    Some(Decimal::from(self))
517                }
518
519                fn saturating_convert(self) -> Decimal {
520                    Decimal::from(self)
521                }
522
523                fn wrapping_convert(self) -> Decimal {
524                    Decimal::from(self)
525                }
526            }
527        )*
528    };
529}
530
531macro_rules! impl_safe_convert_to_decimal_from_uint {
532    ($($src:ty),* $(,)?) => {
533        $(
534            impl SafeConvert<Decimal> for $src {
535                fn checked_convert(self) -> Option<Decimal> {
536                    Some(Decimal::from(self))
537                }
538
539                fn saturating_convert(self) -> Decimal {
540                    Decimal::from(self)
541                }
542
543                fn wrapping_convert(self) -> Decimal {
544                    Decimal::from(self)
545                }
546            }
547        )*
548    };
549}
550
551macro_rules! impl_safe_convert_to_decimal_from_float {
552    ($($src:ty),* $(,)?) => {
553        $(
554            impl SafeConvert<Decimal> for $src {
555                fn checked_convert(self) -> Option<Decimal> {
556                    if !self.is_finite() {
557                        return None;
558                    }
559                    Some(Decimal::from(self))
560                }
561
562                fn saturating_convert(self) -> Decimal {
563                    self.checked_convert()
564                        .unwrap_or_else(|| Decimal::default())
565                }
566
567                fn wrapping_convert(self) -> Decimal {
568                    self.saturating_convert()
569                }
570            }
571        )*
572    };
573}
574
575impl_safe_convert_self!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, f32, f64);
576impl_safe_convert_self!(Int, Uint, Decimal);
577
578pub mod decimal;
579pub mod f32;
580pub mod f64;
581pub mod i128;
582pub mod i16;
583pub mod i32;
584pub mod i64;
585pub mod i8;
586pub mod int;
587pub mod u128;
588pub mod u16;
589pub mod u32;
590pub mod u64;
591pub mod u8;
592pub mod uint;