Skip to main content

reifydb_value/value/number/safe/
sub.rs

1// SPDX-License-Identifier: MIT
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 num_bigint::BigInt;
31
32use crate::value::{decimal::Decimal, int::Int, uint::Uint};
33
34impl SafeSub for Int {
35	fn checked_sub(&self, r: &Self) -> Option<Self> {
36		Some(Int::from(&self.0 - &r.0))
37	}
38
39	fn saturating_sub(&self, r: &Self) -> Self {
40		Int::from(&self.0 - &r.0)
41	}
42
43	fn wrapping_sub(&self, r: &Self) -> Self {
44		Int::from(&self.0 - &r.0)
45	}
46}
47
48impl SafeSub for Uint {
49	fn checked_sub(&self, r: &Self) -> Option<Self> {
50		let result = &self.0 - &r.0;
51		if result < BigInt::from(0) {
52			None
53		} else {
54			Some(Uint::from(result))
55		}
56	}
57
58	fn saturating_sub(&self, r: &Self) -> Self {
59		let result = &self.0 - &r.0;
60		if result < BigInt::from(0) {
61			Uint::from(0u64)
62		} else {
63			Uint::from(result)
64		}
65	}
66
67	fn wrapping_sub(&self, r: &Self) -> Self {
68		let result = &self.0 - &r.0;
69		if result < BigInt::from(0) {
70			Uint::from(0u64)
71		} else {
72			Uint::from(result)
73		}
74	}
75}
76
77impl SafeSub for Decimal {
78	fn checked_sub(&self, r: &Self) -> Option<Self> {
79		let result = self.inner() - r.inner();
80		Some(Decimal::from(result))
81	}
82
83	fn saturating_sub(&self, r: &Self) -> Self {
84		let result = self.inner() - r.inner();
85		Decimal::from(result)
86	}
87
88	fn wrapping_sub(&self, r: &Self) -> Self {
89		let result = self.inner() - r.inner();
90		Decimal::from(result)
91	}
92}
93
94impl SafeSub for f32 {
95	fn checked_sub(&self, r: &Self) -> Option<Self> {
96		let result = *self - *r;
97		if result.is_finite() {
98			Some(result)
99		} else {
100			None
101		}
102	}
103
104	fn saturating_sub(&self, r: &Self) -> Self {
105		let result = *self - *r;
106		if result.is_infinite() {
107			if result.is_sign_negative() {
108				f32::MIN
109			} else {
110				f32::MAX
111			}
112		} else {
113			result
114		}
115	}
116
117	fn wrapping_sub(&self, r: &Self) -> Self {
118		*self - *r
119	}
120}
121
122impl SafeSub for f64 {
123	fn checked_sub(&self, r: &Self) -> Option<Self> {
124		let result = *self - *r;
125		if result.is_finite() {
126			Some(result)
127		} else {
128			None
129		}
130	}
131
132	fn saturating_sub(&self, r: &Self) -> Self {
133		let result = *self - *r;
134		if result.is_infinite() {
135			if result.is_sign_negative() {
136				f64::MIN
137			} else {
138				f64::MAX
139			}
140		} else {
141			result
142		}
143	}
144
145	fn wrapping_sub(&self, r: &Self) -> Self {
146		*self - *r
147	}
148}
149
150#[cfg(test)]
151pub mod tests {
152	macro_rules! define_tests {
153        ($($t:ty => $mod:ident),*) => {
154            $(
155                mod $mod {
156                    use super::super::SafeSub;
157
158                    #[test]
159                    fn checked_sub_happy() {
160                        let x: $t = 20;
161                        let y: $t = 10;
162                        assert_eq!(SafeSub::checked_sub(&x, &y), Some(10));
163                    }
164
165                    #[test]
166                    fn checked_sub_unhappy() {
167                        let x: $t = <$t>::MIN;
168                        let y: $t = 1;
169                        assert_eq!(SafeSub::checked_sub(&x, &y), None);
170                    }
171
172                    #[test]
173                    fn saturating_sub_happy() {
174                        let x: $t = 20;
175                        let y: $t = 10;
176                        assert_eq!(SafeSub::saturating_sub(&x, &y), 10);
177                    }
178
179                    #[test]
180                    fn saturating_sub_unhappy() {
181                        let x: $t = <$t>::MIN;
182                        let y: $t = 1;
183                        assert_eq!(SafeSub::saturating_sub(&x, &y), <$t>::MIN);
184                    }
185
186                    #[test]
187                    fn wrapping_sub_happy() {
188                        let x: $t = 20;
189                        let y: $t = 10;
190                        assert_eq!(SafeSub::wrapping_sub(&x, &y), 10);
191                    }
192
193                    #[test]
194                    fn wrapping_sub_unhappy() {
195                        let x: $t = <$t>::MIN;
196                        let y: $t = 1;
197                        assert_eq!(SafeSub::wrapping_sub(&x, &y), <$t>::MAX);
198                    }
199                }
200            )*
201        };
202    }
203
204	define_tests!(
205	    i8 => i8_tests,
206	    i16 => i16_tests,
207	    i32 => i32_tests,
208	    i64 => i64_tests,
209	    i128 => i128_tests,
210	    u8 => u8_tests,
211	    u16 => u16_tests,
212	    u32 => u32_tests,
213	    u64 => u64_tests,
214	    u128 => u128_tests
215	);
216}