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}