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(|v| v * 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(|v| v * 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 /// If it is [`None`] and `debug_assertions` are enabled, the method panics.
32 /// If `debug_assertions` are disabled, the method is a no-op even if it is [`None`].
33 ///
34 /// # Examples
35 ///
36 /// ```rust
37 /// use chain_assertions::prelude::*;
38 ///
39 /// let x: Option<i32> = Some(21);
40 /// let x = x.debug_assert_some().map(|v| v * 2);
41 /// assert_eq!(x, Some(42));
42 /// ```
43 fn debug_assert_some(self) -> Self;
44}
45
46/// An extension trait to add the assertion_none methods.
47pub trait AssertNoneExt {
48 /// Asserts the Option is [`None`].
49 ///
50 /// # Panics
51 ///
52 /// If it is [`Some`], the method panics.
53 ///
54 /// # Examples
55 ///
56 /// ```rust
57 /// use chain_assertions::prelude::*;
58 ///
59 /// let x: Option<i32> = None;
60 /// let x = x.assert_none().map(|v| v * 2);
61 /// assert_eq!(x, None);
62 /// ```
63 ///
64 /// ```rust,should_panic
65 /// use chain_assertions::prelude::*;
66 ///
67 /// let x: Option<i32> = Some(21);
68 /// let _ = x.assert_none().map(|v| v * 2);
69 /// // ^-- panics here
70 /// ```
71 fn assert_none(self) -> Self;
72
73 /// Asserts the Option is [`None`] only in debug builds.
74 ///
75 /// # Panics
76 ///
77 /// If it is [`Some`] and `debug_assertions` are enabled, the method panics.
78 /// If `debug_assertions` are disabled, the method is a no-op even if it is [`Some`].
79 ///
80 /// # Examples
81 ///
82 /// ```rust
83 /// use chain_assertions::prelude::*;
84 ///
85 /// let x: Option<i32> = None;
86 /// let x = x.debug_assert_none().map(|v| v * 2);
87 /// assert_eq!(x, None);
88 /// ```
89 fn debug_assert_none(self) -> Self;
90}
91
92impl<T> AssertSomeExt for Option<T> {
93 #[track_caller]
94 #[inline]
95 fn assert_some(self) -> Self {
96 if self.is_none() {
97 panic!("Expected Some(_), got None");
98 }
99 self
100 }
101
102 #[track_caller]
103 #[inline]
104 fn debug_assert_some(self) -> Self {
105 #[cfg(all(debug_assertions, not(feature = "passthrough")))]
106 {
107 if self.is_none() {
108 panic!("Expected Some(_), got None");
109 }
110 }
111 self
112 }
113}
114
115impl<T> AssertNoneExt for Option<T>
116where
117 T: crate::fmt::Debug,
118{
119 #[track_caller]
120 #[inline]
121 fn assert_none(self) -> Self {
122 if let Some(ref v) = self {
123 panic!("Expected None, got Some({:?})", v);
124 }
125 self
126 }
127
128 #[track_caller]
129 #[inline]
130 fn debug_assert_none(self) -> Self {
131 #[cfg(all(debug_assertions, not(feature = "passthrough")))]
132 {
133 if let Some(ref v) = self {
134 panic!("Expected None, got Some({:?})", v);
135 }
136 }
137 self
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 struct NonDebuggable;
144
145 #[derive(Debug)]
146 struct Debuggable;
147
148 mod assert_some {
149 use super::{super::*, *};
150
151 #[test]
152 fn it_succeeds_on_some() {
153 let opt: Option<NonDebuggable> = Some(NonDebuggable);
154 let target = opt.assert_some();
155
156 assert!(
157 matches!(target, Some(NonDebuggable)),
158 "Expected Some(NonDebuggable)"
159 );
160 }
161
162 #[test]
163 #[should_panic(expected = "Expected Some(_), got None")]
164 fn it_fails_on_none() {
165 let opt: Option<NonDebuggable> = None;
166 let _ = opt.assert_some();
167 // ^-- should panic here
168 }
169 }
170
171 mod debug_assert_some {
172 use super::{super::*, *};
173
174 #[test]
175 fn it_succeeds_on_some() {
176 let opt: Option<NonDebuggable> = Some(NonDebuggable);
177 let target = opt.debug_assert_some();
178
179 assert!(
180 matches!(target, Some(NonDebuggable)),
181 "Expected Some(NonDebuggable)"
182 );
183 }
184
185 #[test]
186 #[cfg_attr(
187 all(debug_assertions, not(feature = "passthrough")),
188 should_panic(expected = "Expected Some(_), got None")
189 )]
190 fn it_fails_on_none() {
191 let opt: Option<NonDebuggable> = None;
192 let target = opt.debug_assert_some();
193 // ^-- should panic here only in debug mode
194 assert!(matches!(target, None), "Expected None");
195 }
196 }
197
198 mod assert_none {
199 use super::{super::*, *};
200
201 #[test]
202 fn it_succeeds_on_none() {
203 let opt: Option<Debuggable> = None;
204 let target = opt.assert_none();
205
206 assert!(matches!(target, None), "Expected None");
207 }
208
209 #[test]
210 #[should_panic(expected = "Expected None, got Some(Debuggable)")]
211 fn it_fails_on_some() {
212 let opt: Option<Debuggable> = Some(Debuggable);
213 let target = opt.assert_none();
214 // ^-- should panic here only in debug mode
215 assert!(
216 matches!(target, Some(Debuggable)),
217 "Expected Some(Debuggable)"
218 );
219 }
220 }
221
222 mod debug_assert_none {
223 use super::{super::*, *};
224
225 #[test]
226 fn it_succeeds_on_none() {
227 let opt: Option<Debuggable> = None;
228 let target = opt.debug_assert_none();
229
230 assert!(matches!(target, None), "Expected None");
231 }
232
233 #[test]
234 #[cfg_attr(
235 all(debug_assertions, not(feature = "passthrough")),
236 should_panic(expected = "Expected None, got Some(Debuggable)")
237 )]
238 fn it_fails_on_some() {
239 let opt: Option<Debuggable> = Some(Debuggable);
240 let target = opt.debug_assert_none();
241 // ^-- should panic here only in debug mode
242 assert!(
243 matches!(target, Some(Debuggable)),
244 "Expected Some(Debuggable)"
245 );
246 }
247 }
248}