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 Ok({:?})", 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 Ok({:?})", 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_ok_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 Ok(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
290 mod debug_assert_some {
291 use super::{super::*, *};
292
293 #[test]
294 fn it_succeeds_on_some() {
295 let x: Option<NonDebuggable> = Some(NonDebuggable);
296 let x = x.debug_assert_some();
297
298 assert!(
299 matches!(x, Some(NonDebuggable)),
300 "Expected Some(NonDebuggable)"
301 );
302 }
303
304 #[test]
305 #[cfg_attr(
306 all(debug_assertions, not(feature = "passthrough")),
307 should_panic(expected = "Expected Some(_), got None")
308 )]
309 fn it_fails_on_none() {
310 let x: Option<NonDebuggable> = None;
311 let x = x.debug_assert_some();
312 // ^-- should panic here only in debug mode
313 assert!(matches!(x, None), "Expected None");
314 }
315 }
316
317 mod debug_assert_some_and {
318 use super::super::*;
319
320 #[test]
321 fn it_succeeds_on_ok_and_condition_satisfied() {
322 let x: Option<i32> = Some(21);
323 let x = x.debug_assert_some_and(|x| x >= &20).map(|x| x * 2);
324
325 assert_eq!(x, Some(42));
326 }
327
328 #[test]
329 #[cfg_attr(
330 all(debug_assertions, not(feature = "passthrough")),
331 should_panic(expected = "Condition not satisfied for Ok(19)")
332 )]
333 fn it_fails_on_ok_and_condition_not_satisfied() {
334 let x: Option<i32> = Some(19);
335 let x = x.debug_assert_some_and(|x| x >= &20).map(|x| x * 2);
336 // ^-- should panic here only in debug mode
337
338 // for debug builds
339 assert_eq!(x, Some(38));
340 }
341 }
342
343 mod assert_none {
344 use super::{super::*, *};
345
346 #[test]
347 fn it_succeeds_on_none() {
348 let x: Option<Debuggable> = None;
349 let x = x.assert_none();
350
351 assert!(matches!(x, None), "Expected None");
352 }
353
354 #[test]
355 #[should_panic(expected = "Expected None, got Some(Debuggable)")]
356 fn it_fails_on_some() {
357 let x: Option<Debuggable> = Some(Debuggable);
358 let _ = x.assert_none();
359 // ^-- should panic here
360 }
361 }
362
363 mod debug_assert_none {
364 use super::{super::*, *};
365
366 #[test]
367 fn it_succeeds_on_none() {
368 let x: Option<Debuggable> = None;
369 let x = x.debug_assert_none();
370
371 assert!(matches!(x, None), "Expected None");
372 }
373
374 #[test]
375 #[cfg_attr(
376 all(debug_assertions, not(feature = "passthrough")),
377 should_panic(expected = "Expected None, got Some(Debuggable)")
378 )]
379 fn it_fails_on_some() {
380 let x: Option<Debuggable> = Some(Debuggable);
381 let x = x.debug_assert_none();
382 // ^-- should panic here only in debug mode
383
384 // for debug builds
385 assert!(matches!(x, Some(Debuggable)), "Expected Some(Debuggable)");
386 }
387 }
388}