Skip to main content

reifydb_value/value/number/safe/
sub.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4pub trait SafeSub: Sized {
5	fn checked_sub(&self, r: &Self) -> Option<Self>;
6	fn saturating_sub(&self, r: &Self) -> Self;
7	fn wrapping_sub(&self, r: &Self) -> Self;
8}
9
10macro_rules! impl_safe_sub {
11    ($($t:ty),*) => {
12        $(
13            impl SafeSub for $t {
14                fn checked_sub(&self, r: &Self) -> Option<Self> {
15                    <$t>::checked_sub(*self, *r)
16                }
17                fn saturating_sub(&self, r: &Self) -> Self {
18                    <$t>::saturating_sub(*self, *r)
19                }
20                fn wrapping_sub(&self, r: &Self) -> Self {
21                    <$t>::wrapping_sub(*self, *r)
22                }
23            }
24        )*
25    };
26}
27
28impl_safe_sub!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128);
29
30use crate::value::decimal::Decimal;
31
32impl SafeSub for Decimal {
33	fn checked_sub(&self, r: &Self) -> Option<Self> {
34		Decimal::checked_sub(self, r)
35	}
36
37	fn saturating_sub(&self, r: &Self) -> Self {
38		Decimal::saturating_sub(self, r)
39	}
40
41	fn wrapping_sub(&self, r: &Self) -> Self {
42		Decimal::saturating_sub(self, r)
43	}
44}
45
46impl SafeSub for f32 {
47	fn checked_sub(&self, r: &Self) -> Option<Self> {
48		let result = *self - *r;
49		if result.is_infinite() && self.is_finite() && r.is_finite() {
50			None
51		} else {
52			Some(result)
53		}
54	}
55
56	fn saturating_sub(&self, r: &Self) -> Self {
57		let result = *self - *r;
58		if result.is_infinite() {
59			if result.is_sign_negative() {
60				f32::MIN
61			} else {
62				f32::MAX
63			}
64		} else {
65			result
66		}
67	}
68
69	fn wrapping_sub(&self, r: &Self) -> Self {
70		*self - *r
71	}
72}
73
74impl SafeSub for f64 {
75	fn checked_sub(&self, r: &Self) -> Option<Self> {
76		let result = *self - *r;
77		if result.is_infinite() && self.is_finite() && r.is_finite() {
78			None
79		} else {
80			Some(result)
81		}
82	}
83
84	fn saturating_sub(&self, r: &Self) -> Self {
85		let result = *self - *r;
86		if result.is_infinite() {
87			if result.is_sign_negative() {
88				f64::MIN
89			} else {
90				f64::MAX
91			}
92		} else {
93			result
94		}
95	}
96
97	fn wrapping_sub(&self, r: &Self) -> Self {
98		*self - *r
99	}
100}
101
102#[cfg(test)]
103pub mod tests {
104	macro_rules! define_tests {
105        ($($t:ty => $mod:ident),*) => {
106            $(
107                mod $mod {
108                    use super::super::SafeSub;
109
110                    #[test]
111                    fn checked_sub_happy() {
112                        let x: $t = 20;
113                        let y: $t = 10;
114                        assert_eq!(SafeSub::checked_sub(&x, &y), Some(10));
115                    }
116
117                    #[test]
118                    fn checked_sub_unhappy() {
119                        let x: $t = <$t>::MIN;
120                        let y: $t = 1;
121                        assert_eq!(SafeSub::checked_sub(&x, &y), None);
122                    }
123
124                    #[test]
125                    fn saturating_sub_happy() {
126                        let x: $t = 20;
127                        let y: $t = 10;
128                        assert_eq!(SafeSub::saturating_sub(&x, &y), 10);
129                    }
130
131                    #[test]
132                    fn saturating_sub_unhappy() {
133                        let x: $t = <$t>::MIN;
134                        let y: $t = 1;
135                        assert_eq!(SafeSub::saturating_sub(&x, &y), <$t>::MIN);
136                    }
137
138                    #[test]
139                    fn wrapping_sub_happy() {
140                        let x: $t = 20;
141                        let y: $t = 10;
142                        assert_eq!(SafeSub::wrapping_sub(&x, &y), 10);
143                    }
144
145                    #[test]
146                    fn wrapping_sub_unhappy() {
147                        let x: $t = <$t>::MIN;
148                        let y: $t = 1;
149                        assert_eq!(SafeSub::wrapping_sub(&x, &y), <$t>::MAX);
150                    }
151                }
152            )*
153        };
154    }
155
156	define_tests!(
157	    i8 => i8_tests,
158	    i16 => i16_tests,
159	    i32 => i32_tests,
160	    i64 => i64_tests,
161	    i128 => i128_tests,
162	    u8 => u8_tests,
163	    u16 => u16_tests,
164	    u32 => u32_tests,
165	    u64 => u64_tests,
166	    u128 => u128_tests
167	);
168}