1#![cfg_attr(doc, doc = include_str!("../README.md"))]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4pub mod assertion;
5pub use assertion::{AssertAll, Assertion};
6
7use assertion::{BinaryAssertion, UnaryAssertion};
8
9use core::fmt::{Debug, Display};
10use core::ops::Deref;
11
12#[derive(Debug, Clone, Copy)]
17#[must_use = "this blank assertion will do nothing unless it is tested"]
18pub struct Assert<T> {
19 this: T,
20 info: AssertInfo,
21}
22
23#[derive(Debug, Clone, Copy)]
25pub struct AssertInfo {
26 file: &'static str,
27 line: u32,
28 column: u32,
29}
30
31impl AssertInfo {
32 const fn reassert<T>(self, val: T) -> Assert<T> {
33 Assert {
34 this: val,
35 info: self,
36 }
37 }
38}
39
40impl Display for AssertInfo {
41 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
42 let Self { file, line, column } = *self;
43 write!(f, "{file}:{line}:{column}")
44 }
45}
46
47#[macro_export]
56macro_rules! assert_that {
57 ($this:expr) => {
58 $crate::Assert::manual_constructor(
59 $this,
60 ::core::file!(),
61 ::core::line!(),
62 ::core::column!(),
63 )
64 };
65}
66
67impl<T> Assert<T> {
68 #[doc(hidden)]
69 pub const fn manual_constructor(this: T, file: &'static str, line: u32, column: u32) -> Self {
72 Self {
73 this,
74 info: AssertInfo { file, line, column },
75 }
76 }
77
78 pub const fn as_ref(&self) -> Assert<&T> {
80 Assert {
81 this: &self.this,
82 info: self.info,
83 }
84 }
85
86 pub fn as_deref<U>(&self) -> Assert<&U>
88 where
89 T: Deref<Target = U>,
90 {
91 Assert {
92 this: &self.this,
93 info: self.info,
94 }
95 }
96
97 pub fn map<F, U>(self, f: F) -> Assert<U>
99 where
100 F: FnOnce(T) -> U,
101 {
102 self.info.reassert(f(self.this))
103 }
104
105 pub fn is_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
107 where
108 T: PartialEq<U> + Debug,
109 U: Debug,
110 {
111 let test_result = self.this == other;
112 BinaryAssertion::new(self.this, other, test_result, "lhs == rhs", self.info)
113 }
114
115 pub fn is_not_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
117 where
118 T: PartialEq<U> + Debug,
119 U: Debug,
120 {
121 let test_result = self.this != other;
122 BinaryAssertion::new(self.this, other, test_result, "lhs != rhs", self.info)
123 }
124
125 pub fn is_greater_than<U>(self, other: U) -> BinaryAssertion<T, U>
127 where
128 T: PartialOrd<U> + Debug,
129 U: Debug,
130 {
131 let test_result = self.this > other;
132 BinaryAssertion::new(self.this, other, test_result, "lhs > rhs", self.info)
133 }
134
135 pub fn is_less_than<U>(self, other: U) -> BinaryAssertion<T, U>
137 where
138 T: PartialOrd<U> + Debug,
139 U: Debug,
140 {
141 let test_result = self.this < other;
142 BinaryAssertion::new(self.this, other, test_result, "lhs < rhs", self.info)
143 }
144
145 pub fn is_greater_than_or_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
147 where
148 T: PartialOrd<U> + Debug,
149 U: Debug,
150 {
151 let test_result = self.this >= other;
152 BinaryAssertion::new(self.this, other, test_result, "lhs >= rhs", self.info)
153 }
154
155 pub fn is_less_than_or_equal_to<U>(self, other: U) -> BinaryAssertion<T, U>
157 where
158 T: PartialOrd<U> + Debug,
159 U: Debug,
160 {
161 let test_result = self.this <= other;
162 BinaryAssertion::new(self.this, other, test_result, "lhs <= rhs", self.info)
163 }
164
165 pub fn is_in(self, container: &[T]) -> BinaryAssertion<&[T], T>
167 where
168 T: PartialEq,
169 {
170 self.info.reassert(container).contains(self.this)
171 }
172
173 pub fn is_not_in(self, container: &[T]) -> BinaryAssertion<&[T], T>
175 where
176 T: PartialEq,
177 {
178 self.info.reassert(container).does_not_contain(self.this)
179 }
180}
181
182impl<T> Assert<&T> {
183 pub fn cloned(self) -> Assert<T>
185 where
186 T: Clone,
187 {
188 Assert {
189 this: self.this.clone(),
190 info: self.info,
191 }
192 }
193
194 pub const fn copied(self) -> Assert<T>
196 where
197 T: Copy,
198 {
199 Assert {
200 this: *self.this,
201 info: self.info,
202 }
203 }
204}
205
206impl Assert<bool> {
207 pub const fn is_true(self) -> UnaryAssertion<bool> {
209 UnaryAssertion::new(self.this, self.this, "this.is_true()", self.info)
210 }
211
212 pub const fn is_false(self) -> UnaryAssertion<bool> {
214 UnaryAssertion::new(self.this, !self.this, "this.is_false()", self.info)
215 }
216}
217
218impl<T: Debug> Assert<Option<T>> {
219 pub fn is_some(self) -> UnaryAssertion<Option<T>> {
221 let test_result = self.this.is_some();
222 UnaryAssertion::new(self.this, test_result, "this.is_some()", self.info)
223 }
224
225 pub fn is_none(self) -> UnaryAssertion<Option<T>> {
227 let test_result = self.this.is_none();
228 UnaryAssertion::new(self.this, test_result, "this.is_none()", self.info)
229 }
230
231 pub fn is_some_and<F, A>(self, f: F) -> UnaryAssertion<Option<T>>
233 where
234 F: Fn(Assert<&T>) -> A,
235 A: Assertion,
236 {
237 let test_result = self
238 .this
239 .as_ref()
240 .is_some_and(|opt| f(self.info.reassert(opt)).test());
241 UnaryAssertion::new(self.this, test_result, "this.is_some_and(f)", self.info)
242 }
243
244 pub fn is_none_or<F, A>(self, f: F) -> UnaryAssertion<Option<T>>
246 where
247 F: Fn(Assert<&T>) -> A,
248 A: Assertion,
249 {
250 let test_result = self
251 .this
252 .as_ref()
253 .is_none_or(|opt| f(self.info.reassert(opt)).test());
254 UnaryAssertion::new(self.this, test_result, "this.is_none_or(f)", self.info)
255 }
256}
257
258impl<T: Debug, E: Debug> Assert<Result<T, E>> {
259 pub fn is_ok(self) -> UnaryAssertion<Result<T, E>> {
261 let test_result = self.this.is_ok();
262 UnaryAssertion::new(self.this, test_result, "this.is_ok()", self.info)
263 }
264
265 pub fn is_err(self) -> UnaryAssertion<Result<T, E>> {
267 let test_result = self.this.is_err();
268 UnaryAssertion::new(self.this, test_result, "this.is_err()", self.info)
269 }
270
271 pub fn is_ok_and<F, A>(self, f: F) -> impl Assertion
273 where
274 F: Fn(Assert<&T>) -> A,
275 A: Assertion,
276 {
277 let test_result = self
278 .this
279 .as_ref()
280 .is_ok_and(|o| f(self.info.reassert(o)).test());
281 UnaryAssertion::new(self.this, test_result, "this.is_ok_and(f)", self.info)
282 }
283
284 pub fn is_err_and<F, A>(self, f: F) -> UnaryAssertion<Result<T, E>>
286 where
287 F: Fn(Assert<&E>) -> A,
288 A: Assertion,
289 {
290 let test_result = self
291 .this
292 .as_ref()
293 .is_err_and(|e| f(self.info.reassert(e)).test());
294 UnaryAssertion::new(self.this, test_result, "this.is_err_and(f)", self.info)
295 }
296}
297
298impl<'a, T> Assert<&'a [T]> {
299 pub fn is_sorted(self) -> UnaryAssertion<&'a [T]>
301 where
302 T: PartialOrd,
303 {
304 let test_result = self.this.is_sorted();
305 UnaryAssertion::new(self.this, test_result, "this.is_sorted()", self.info)
306 }
307
308 pub fn is_not_sorted(self) -> UnaryAssertion<&'a [T]>
310 where
311 T: PartialOrd,
312 {
313 let test_result = !self.this.is_sorted();
314 UnaryAssertion::new(self.this, test_result, "!this.is_sorted()", self.info)
315 }
316
317 pub fn is_sorted_by<'b, F>(self, compare: F) -> UnaryAssertion<&'a [T]>
319 where
320 F: FnMut(&'b T, &'b T) -> bool,
321 'a: 'b, {
323 let test_result = self.this.is_sorted_by(compare);
324 UnaryAssertion::new(self.this, test_result, "this.is_sorted_by()", self.info)
325 }
326
327 pub fn is_not_sorted_by<'b, F>(self, compare: F) -> UnaryAssertion<&'a [T]>
329 where
330 F: FnMut(&'b T, &'b T) -> bool,
331 'a: 'b, {
333 let test_result = !self.this.is_sorted_by(compare);
334 UnaryAssertion::new(self.this, test_result, "!this.is_sorted_by()", self.info)
335 }
336
337 pub fn is_sorted_by_key<'b, F, K>(self, f: F) -> UnaryAssertion<&'a [T]>
339 where
340 F: FnMut(&'b T) -> K,
341 K: PartialOrd,
342 'a: 'b,
343 {
344 let test_result = self.this.is_sorted_by_key(f);
345 UnaryAssertion::new(self.this, test_result, "this.is_sorted_by_key()", self.info)
346 }
347
348 pub fn is_not_sorted_by_key<'b, F, K>(self, f: F) -> UnaryAssertion<&'a [T]>
350 where
351 F: FnMut(&'b T) -> K,
352 K: PartialOrd,
353 'a: 'b,
354 {
355 let test_result = !self.this.is_sorted_by_key(f);
356 UnaryAssertion::new(
357 self.this,
358 test_result,
359 "!this.is_sorted_by_key()",
360 self.info,
361 )
362 }
363
364 pub const fn is_empty(self) -> UnaryAssertion<&'a [T]> {
366 let test_result = self.this.is_empty();
367 UnaryAssertion::new(self.this, test_result, "this.is_empty()", self.info)
368 }
369
370 pub const fn is_not_empty(self) -> UnaryAssertion<&'a [T]> {
372 let test_result = !self.this.is_empty();
373 UnaryAssertion::new(self.this, test_result, "!this.is_empty()", self.info)
374 }
375
376 pub const fn is_len(self, len: usize) -> BinaryAssertion<&'a [T], usize> {
378 let test_result = self.this.len() == len;
379 BinaryAssertion::new(self.this, len, test_result, "lhs.is_len(rhs)", self.info)
380 }
381
382 pub const fn is_not_len(self, len: usize) -> BinaryAssertion<&'a [T], usize> {
384 let test_result = self.this.len() != len;
385 BinaryAssertion::new(self.this, len, test_result, "!lhs.is_len(rhs)", self.info)
386 }
387
388 pub fn contains(self, elem: T) -> BinaryAssertion<&'a [T], T>
390 where
391 T: PartialEq,
392 {
393 let test_result = self.this.contains(&elem);
394 BinaryAssertion::new(self.this, elem, test_result, "lhs.contains(rhs)", self.info)
395 }
396
397 pub fn does_not_contain(self, elem: T) -> BinaryAssertion<&'a [T], T>
399 where
400 T: PartialEq,
401 {
402 let test_result = !self.this.contains(&elem);
403 BinaryAssertion::new(
404 self.this,
405 elem,
406 test_result,
407 "!lhs.contains(rhs)",
408 self.info,
409 )
410 }
411}