chain_assertions/option.rs
1/// An extension trait to add the assertion_some methods.
2pub trait AssertSomeExt {
3 /// Asserts the [`Option`] is [`Some`].
4 ///
5 /// # Panics
6 ///
7 /// If it is [`None`], the method panics.
8 ///
9 /// # Examples
10 ///
11 /// ```rust
12 /// use chain_assertions::prelude::*;
13 ///
14 /// let x: Option<i32> = Some(21);
15 /// let x = x.assert_some().map(|x| x * 2);
16 /// assert_eq!(x, Some(42));
17 /// ```
18 ///
19 /// ```rust,should_panic
20 /// use chain_assertions::prelude::*;
21 ///
22 /// let x: Option<i32> = None;
23 /// let _ = x.assert_some().map(|x| x * 2);
24 /// // ^-- panics here
25 fn assert_some(self) -> Self;
26
27 /// Asserts the [`Option`] is [`Some`] only in debug builds.
28 ///
29 /// # Panics
30 ///
31 /// The method panics if all following conditions are satisfied:
32 ///
33 /// - It is [`None`]
34 /// - `debug_assertions` is enabled
35 /// - `passthrough` feature is disabled
36 ///
37 /// Otherwise, the method returns self as is.
38 ///
39 /// # Examples
40 ///
41 /// ```rust
42 /// use chain_assertions::prelude::*;
43 ///
44 /// let x: Option<i32> = Some(21);
45 /// let x = x.debug_assert_some().map(|x| x * 2);
46 /// assert_eq!(x, Some(42));
47 /// ```
48 fn debug_assert_some(self) -> Self;
49}
50
51/// An extension trait to add the assertion_some_and methods.
52pub trait AssertSomeAndExt<T> {
53 /// Asserts the [`Option`] is [`Some`] and satisfies the condition.
54 ///
55 /// # Panics
56 ///
57 /// If it is [`None`] or the condition is not satisfied, the method panics.
58 ///
59 /// # Examples
60 ///
61 /// ```rust
62 /// use chain_assertions::prelude::*;
63 ///
64 /// let x: Option<i32> = Some(21);
65 /// let x = x.assert_some_and(|x| x >= &20).map(|x| x * 2);
66 /// assert_eq!(x, Some(42));
67 /// ```
68 ///
69 /// ```rust,should_panic
70 /// use chain_assertions::prelude::*;
71 ///
72 /// let x: Option<i32> = Some(19);
73 /// let _ = x.assert_some_and(|x| x >= &20).map(|x| x * 2);
74 /// // ^-- panics here
75 /// ```
76 fn assert_some_and(self, cond: impl FnOnce(&T) -> bool) -> Self;
77
78 /// Asserts the [`Option`] is [`Some`] and satisfies the condition only in debug builds.
79 ///
80 /// # Panics
81 ///
82 /// The method panics if all following conditions are satisfied:
83 ///
84 /// - It is [`None`] or the condition is not satisfied
85 /// - `debug_assertions` is enabled
86 /// - `passthrough` feature is disabled
87 ///
88 /// Otherwise, the method returns self as is.
89 ///
90 /// # Examples
91 ///
92 /// ```rust
93 /// use chain_assertions::prelude::*;
94 ///
95 /// let x: Option<i32> = Some(21);
96 /// let x = x.debug_assert_some_and(|x| x >= &20).map(|x| x * 2);
97 /// assert_eq!(x, Some(42));
98 /// ```
99 ///
100 /// ```rust,should_panic,ignore
101 /// use chain_assertions::prelude::*;
102 ///
103 /// let x: Option<i32> = Some(19);
104 /// let _ = x.debug_assert_some_and(|x| x >= &20).map
105 /// // ^-- panics here if debug_assertion is enabled
106 /// ```
107 fn debug_assert_some_and(self, cond: impl FnOnce(&T) -> bool) -> Self;
108}
109
110/// An extension trait to add the assertion_none methods.
111pub trait AssertNoneExt {
112 /// Asserts the [`Option`] is [`None`].
113 ///
114 /// # Panics
115 ///
116 /// If it is [`Some`], the method panics.
117 ///
118 /// # Examples
119 ///
120 /// ```rust
121 /// use chain_assertions::prelude::*;
122 ///
123 /// let x: Option<i32> = None;
124 /// let x = x.assert_none().map(|x| x * 2);
125 /// assert_eq!(x, None);
126 /// ```
127 ///
128 /// ```rust,should_panic
129 /// use chain_assertions::prelude::*;
130 ///
131 /// let x: Option<i32> = Some(21);
132 /// let _ = x.assert_none().map(|x| x * 2);
133 /// // ^-- panics here
134 /// ```
135 fn assert_none(self) -> Self;
136
137 /// Asserts the [`Option`] is [`None`] only in debug builds.
138 ///
139 /// # Panics
140 ///
141 /// The method panics if all following conditions are satisfied:
142 ///
143 /// - It is [`Some`]
144 /// - `debug_assertions` is enabled
145 /// - `passthrough` feature is disabled
146 ///
147 /// Otherwise, the method returns self as is.
148 ///
149 /// # Examples
150 ///
151 /// ```rust
152 /// use chain_assertions::prelude::*;
153 ///
154 /// let x: Option<i32> = None;
155 /// let x = x.debug_assert_none().map(|v| v * 2);
156 /// assert_eq!(x, None);
157 /// ```
158 fn debug_assert_none(self) -> Self;
159}
160
161impl<T> AssertSomeExt for Option<T> {
162 #[track_caller]
163 #[inline]
164 fn assert_some(self) -> Self {
165 if self.is_none() {
166 panic!("Expected Some(_), got None");
167 }
168 self
169 }
170
171 #[track_caller]
172 #[inline]
173 fn debug_assert_some(self) -> Self {
174 #[cfg(all(debug_assertions, not(feature = "passthrough")))]
175 {
176 if self.is_none() {
177 panic!("Expected Some(_), got None");
178 }
179 }
180 self
181 }
182}
183
184impl<T> AssertSomeAndExt<T> for Option<T>
185where
186 T: crate::fmt::Debug,
187{
188 #[track_caller]
189 #[inline]
190 fn assert_some_and(self, cond: impl FnOnce(&T) -> bool) -> Self {
191 match self {
192 Some(ref v) if cond(v) => { /* do nothing */ }
193 Some(ref v) => panic!("Condition not satisfied for Some({:?})", v),
194 None => panic!("Expected Some(_), got None"),
195 }
196 self
197 }
198
199 #[track_caller]
200 #[inline]
201 fn debug_assert_some_and(self, _cond: impl FnOnce(&T) -> bool) -> Self {
202 #[cfg(all(debug_assertions, not(feature = "passthrough")))]
203 {
204 match self {
205 Some(ref v) if _cond(v) => { /* do nothing */ }
206 Some(ref v) => panic!("Condition not satisfied for Some({:?})", v),
207 None => panic!("Expected Some(_), got None"),
208 }
209 }
210 self
211 }
212}
213
214impl<T> AssertNoneExt for Option<T>
215where
216 T: crate::fmt::Debug,
217{
218 #[track_caller]
219 #[inline]
220 fn assert_none(self) -> Self {
221 if let Some(ref v) = self {
222 panic!("Expected None, got Some({:?})", v);
223 }
224 self
225 }
226
227 #[track_caller]
228 #[inline]
229 fn debug_assert_none(self) -> Self {
230 #[cfg(all(debug_assertions, not(feature = "passthrough")))]
231 {
232 if let Some(ref v) = self {
233 panic!("Expected None, got Some({:?})", v);
234 }
235 }
236 self
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 #[derive(PartialEq)]
243 struct NonDebuggable;
244
245 #[derive(Debug, PartialEq)]
246 struct Debuggable;
247
248 mod assert_some {
249 use super::{super::*, *};
250
251 #[test]
252 fn it_succeeds_on_some() {
253 let x: Option<NonDebuggable> = Some(NonDebuggable);
254 let x = x.assert_some();
255
256 assert!(
257 matches!(x, Some(NonDebuggable)),
258 "Expected Some(NonDebuggable)"
259 );
260 }
261
262 #[test]
263 #[should_panic(expected = "Expected Some(_), got None")]
264 fn it_fails_on_none() {
265 let opt: Option<NonDebuggable> = None;
266 let _ = opt.assert_some();
267 // ^-- should panic here
268 }
269 }
270
271 mod assert_some_and {
272 use super::super::*;
273
274 #[test]
275 fn it_succeeds_on_ok_and_condition_satisfied() {
276 let x: Option<i32> = Some(21);
277 let x = x.assert_some_and(|x| x >= &20).map(|x| x * 2);
278 assert_eq!(x, Some(42));
279 }
280
281 #[test]
282 #[should_panic(expected = "Condition not satisfied for Some(19)")]
283 fn it_fails_on_ok_and_condition_not_satisfied() {
284 let x: Option<i32> = Some(19);
285 let _ = x.assert_some_and(|x| x >= &20).map(|x| x * 2);
286 // ^-- should panic here
287 }
288
289 #[test]
290 #[should_panic(expected = "Expected Some(_), got None")]
291 fn it_fails_on_none() {
292 let x: Option<i32> = None;
293 let _ = x.assert_some_and(|x| x >= &20).map(|x| x * 2);
294 // ^-- should panic here
295 }
296 }
297
298 mod debug_assert_some {
299 use super::{super::*, *};
300
301 #[test]
302 fn it_succeeds_on_some() {
303 let x: Option<NonDebuggable> = Some(NonDebuggable);
304 let x = x.debug_assert_some();
305
306 assert!(
307 matches!(x, Some(NonDebuggable)),
308 "Expected Some(NonDebuggable)"
309 );
310 }
311
312 #[test]
313 #[cfg_attr(
314 all(debug_assertions, not(feature = "passthrough")),
315 should_panic(expected = "Expected Some(_), got None")
316 )]
317 fn it_fails_on_none() {
318 let x: Option<NonDebuggable> = None;
319 let x = x.debug_assert_some();
320 // ^-- should panic here only in debug mode
321 assert!(matches!(x, None), "Expected None");
322 }
323 }
324
325 mod debug_assert_some_and {
326 use super::super::*;
327
328 #[test]
329 fn it_succeeds_on_some_and_condition_satisfied() {
330 let x: Option<i32> = Some(21);
331 let x = x.debug_assert_some_and(|x| x >= &20).map(|x| x * 2);
332
333 assert_eq!(x, Some(42));
334 }
335
336 #[test]
337 #[cfg_attr(
338 all(debug_assertions, not(feature = "passthrough")),
339 should_panic(expected = "Condition not satisfied for Some(19)")
340 )]
341 fn it_fails_on_some_and_condition_not_satisfied() {
342 let x: Option<i32> = Some(19);
343 let x = x.debug_assert_some_and(|x| x >= &20).map(|x| x * 2);
344 // ^-- should panic here only in debug mode
345
346 // for debug builds
347 assert_eq!(x, Some(38));
348 }
349
350 #[test]
351 #[cfg_attr(
352 all(debug_assertions, not(feature = "passthrough")),
353 should_panic(expected = "Expected Some(_), got None")
354 )]
355 fn it_fails_on_none() {
356 let x: Option<i32> = None;
357 let _ = x.debug_assert_some_and(|x| x >= &20).map(|x| x * 2);
358 // ^-- should panic here
359
360 // for debug builds
361 assert_eq!(x, None);
362 }
363 }
364
365 mod assert_none {
366 use super::{super::*, *};
367
368 #[test]
369 fn it_succeeds_on_none() {
370 let x: Option<Debuggable> = None;
371 let x = x.assert_none();
372
373 assert!(matches!(x, None), "Expected None");
374 }
375
376 #[test]
377 #[should_panic(expected = "Expected None, got Some(Debuggable)")]
378 fn it_fails_on_some() {
379 let x: Option<Debuggable> = Some(Debuggable);
380 let _ = x.assert_none();
381 // ^-- should panic here
382 }
383 }
384
385 mod debug_assert_none {
386 use super::{super::*, *};
387
388 #[test]
389 fn it_succeeds_on_none() {
390 let x: Option<Debuggable> = None;
391 let x = x.debug_assert_none();
392
393 assert!(matches!(x, None), "Expected None");
394 }
395
396 #[test]
397 #[cfg_attr(
398 all(debug_assertions, not(feature = "passthrough")),
399 should_panic(expected = "Expected None, got Some(Debuggable)")
400 )]
401 fn it_fails_on_some() {
402 let x: Option<Debuggable> = Some(Debuggable);
403 let x = x.debug_assert_none();
404 // ^-- should panic here only in debug mode
405
406 // for debug builds
407 assert!(matches!(x, Some(Debuggable)), "Expected Some(Debuggable)");
408 }
409 }
410}