1#![doc = include_str!("../README.md")]
2
3use std::{
4 num,
5 ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Rem, Sub},
6};
7
8use clamp::ValueRangeInclusive;
9pub mod clamp;
10pub mod guard;
11pub mod view;
12
13mod reexports {
14 #[doc(hidden)]
15 pub use anyhow::{anyhow, bail, ensure, format_err, Chain, Context, Error, Result};
16 #[doc(hidden)]
17 pub use serde;
18}
19
20pub mod prelude {
21 pub use crate::reexports::*;
22
23 pub use crate::{
24 clamp::*, commit_or_bail, view::*, Behavior, InherentBehavior, InherentLimits,
25 OpBehaviorParams,
26 };
27
28 pub use checked_rs_macros::clamped;
29}
30
31#[derive(Debug, Clone)]
32pub enum OpBehaviorParams<T: 'static + Copy + Eq + Ord + InherentLimits<T>> {
33 Simple {
34 min: T,
35 max: T,
36 },
37 ExactsOnly(&'static [T]),
38 RangesOnly(&'static [ValueRangeInclusive<T>]),
39 ExactsAndRanges {
40 exacts: &'static [T],
41 ranges: &'static [ValueRangeInclusive<T>],
42 },
43}
44
45pub trait Behavior: Copy + 'static {
46 fn add<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
47 lhs: T,
48 rhs: T,
49 params: OpBehaviorParams<T>,
50 ) -> T
51 where
52 T: Add<Output = T>,
53 T::Output: Eq + Ord + Into<T>,
54 num::Saturating<T>: Add<Output = num::Saturating<T>>,
55 <num::Saturating<T> as Add>::Output: Eq + Ord + Into<num::Saturating<T>>;
56 fn sub<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
57 lhs: T,
58 rhs: T,
59 params: OpBehaviorParams<T>,
60 ) -> T
61 where
62 T: Sub<Output = T>,
63 T::Output: Eq + Ord + Into<T>,
64 num::Saturating<T>: Sub<Output = num::Saturating<T>>,
65 <num::Saturating<T> as Sub>::Output: Eq + Ord + Into<num::Saturating<T>>;
66 fn mul<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
67 lhs: T,
68 rhs: T,
69 params: OpBehaviorParams<T>,
70 ) -> T
71 where
72 T: Mul<Output = T>,
73 T::Output: Eq + Ord + Into<T>,
74 num::Saturating<T>: Mul<Output = num::Saturating<T>>,
75 <num::Saturating<T> as Mul>::Output: Eq + Ord + Into<num::Saturating<T>>;
76 fn div<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
77 lhs: T,
78 rhs: T,
79 params: OpBehaviorParams<T>,
80 ) -> T
81 where
82 T: Div<Output = T>,
83 T::Output: Eq + Ord + Into<T>,
84 num::Saturating<T>: Div<Output = num::Saturating<T>>,
85 <num::Saturating<T> as Div>::Output: Eq + Ord + Into<num::Saturating<T>>;
86 fn rem<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
87 lhs: T,
88 rhs: T,
89 params: OpBehaviorParams<T>,
90 ) -> T
91 where
92 T: Rem<Output = T> + Sub<Output = T>,
93 <T as Rem>::Output: Eq + Ord + Into<T>,
94 <T as Sub>::Output: Eq + Ord + Into<T>,
95 num::Saturating<T>: Rem<Output = num::Saturating<T>>,
96 <num::Saturating<T> as Rem>::Output: Eq + Ord + Into<num::Saturating<T>>;
97 fn bitand<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
98 lhs: T,
99 rhs: T,
100 params: OpBehaviorParams<T>,
101 ) -> T
102 where
103 T: BitAnd<Output = T> + Sub<Output = T>,
104 <T as BitAnd>::Output: Eq + Ord + Into<T>,
105 <T as Sub>::Output: Eq + Ord + Into<T>,
106 num::Saturating<T>: BitAnd<Output = num::Saturating<T>>,
107 <num::Saturating<T> as BitAnd>::Output: Eq + Ord + Into<num::Saturating<T>>;
108 fn bitor<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
109 lhs: T,
110 rhs: T,
111 params: OpBehaviorParams<T>,
112 ) -> T
113 where
114 T: BitOr<Output = T> + Sub<Output = T>,
115 <T as BitOr>::Output: Eq + Ord + Into<T>,
116 <T as Sub>::Output: Eq + Ord + Into<T>,
117 num::Saturating<T>: BitOr<Output = num::Saturating<T>>,
118 <num::Saturating<T> as BitOr>::Output: Eq + Ord + Into<num::Saturating<T>>;
119 fn bitxor<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
120 lhs: T,
121 rhs: T,
122 params: OpBehaviorParams<T>,
123 ) -> T
124 where
125 T: BitXor<Output = T> + Sub<Output = T>,
126 <T as BitXor>::Output: Eq + Ord + Into<T>,
127 <T as Sub>::Output: Eq + Ord + Into<T>,
128 num::Saturating<T>: BitXor<Output = num::Saturating<T>>,
129 <num::Saturating<T> as BitXor>::Output: Eq + Ord + Into<num::Saturating<T>>;
130 fn neg<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
131 val: T,
132 params: OpBehaviorParams<T>,
133 ) -> T
134 where
135 T: Neg<Output = T> + Sub<Output = T>,
136 <T as Neg>::Output: Eq + Ord + Into<T>,
137 <T as Sub>::Output: Eq + Ord + Into<T>,
138 num::Saturating<T>: Neg<Output = num::Saturating<T>>,
139 <num::Saturating<T> as Neg>::Output: Eq + Ord + Into<num::Saturating<T>>;
140 fn not<T: 'static + Copy + Eq + Ord + InherentLimits<T>>(
141 val: T,
142 params: OpBehaviorParams<T>,
143 ) -> T
144 where
145 T: Not<Output = T> + Sub<Output = T>,
146 <T as Not>::Output: Eq + Ord + Into<T>,
147 <T as Sub>::Output: Eq + Ord + Into<T>,
148 num::Saturating<T>: Not<Output = num::Saturating<T>>,
149 <num::Saturating<T> as Not>::Output: Eq + Ord + Into<num::Saturating<T>>;
150}
151
152pub trait InherentLimits<T>: 'static {
153 const MIN: Self;
154 const MAX: Self;
155 const MIN_INT: T;
156 const MAX_INT: T;
157
158 fn is_zero(&self) -> bool;
159 fn is_negative(&self) -> bool;
160 fn is_positive(&self) -> bool;
161}
162
163pub trait InherentBehavior: 'static {
164 type Behavior: Behavior;
165}
166
167#[cfg(test)]
168mod tests {
169 use crate::prelude::*;
170
171 clamped! {
172 #[usize; derive(Debug)]
173 enum DoubleSentinel {
174 Zero(0),
175 Valid(..),
176 Invalid(usize::MAX),
177 }
178 }
179
180 #[test]
181 fn test_enum_simple() {
182 let value = DoubleSentinel::new(0);
183
184 assert!(value.is_some());
185
186 let mut value = value.unwrap();
187
188 value += 1;
189
190 assert_eq!(value, 1);
191 assert!(value.is_valid());
192
193 value -= 1;
194 assert!(value.is_zero());
195
196 value += usize::MAX;
197 assert!(value.is_invalid());
198 }
199
200 clamped! {
201 #[isize; derive(Debug)]
202 enum SignedNumbers {
203 Min(isize::MIN),
204 Neg(..0),
205 Zero(0),
206 Pos(0..),
207 Max(isize::MAX),
208 }
209 }
210
211 clamped! {
246 #[usize]
247 enum ResponseCode {
248 Success[200..300] {
249 Okay(200),
250 Created(201),
251 Accepted(202),
252 Unknown(..),
253 },
254 Error {
255 Client[400..500] {
256 BadRequest(400),
257 Unauthorized(401),
258 PaymentRequired(402),
259 Forbidden(403),
260 NotFound(404),
261 Unknown(..)
262 },
263 Server[500..600] {
264 Internal(500),
265 NotImplemented(501),
266 BadGateway(502),
267 ServiceUnavailable(503),
268 GatewayTimeout(504),
269 Unknown(..)
270 }
271 }
272 }
273 }
274
275 #[test]
276 fn test_enum_nested() {}
277
278 clamped! {
287 #[usize as Hard; derive(Debug)]
288 struct TenOrMore(10..);
289 }
290
291 #[test]
292 fn test_struct_hard() {
293 let value = TenOrMore::new(10);
294
295 assert!(value.is_some());
296
297 let mut value = value.unwrap();
298
299 value += 1;
300
301 assert_eq!(value, 11);
302 }
303
304 #[test]
305 #[should_panic]
306 fn test_struct_hard_overflow() {
307 let value = TenOrMore::new(10);
308
309 assert!(value.is_some());
310
311 let mut value = value.unwrap();
312
313 value -= 1;
314 }
315
316 clamped! {
317 #[usize as Hard; derive(Debug)]
318 struct LessThanTenOrBetween999And2000(..10, 1000..2000);
319 }
320
321 #[test]
322 fn test_struct_multiple_ranges() {
323 let value = LessThanTenOrBetween999And2000::new(5);
324
325 assert!(value.is_some());
326
327 let mut value = value.unwrap();
328
329 value += 3;
330
331 assert_eq!(value, 8);
332
333 value += 1000;
334
335 assert_eq!(value, 1008);
336 }
337}