Skip to main content

assert_rs/
lib.rs

1#![cfg_attr(doc, doc = include_str!("../README.md"))]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4pub mod assertion;
5pub use assertion::Assertion;
6
7use assertion::{BinaryAssertion, UnaryAssertion};
8
9use core::fmt::{Debug, Display};
10use core::ops::Deref;
11
12/// A wrapper around some object that can be reasoned about.
13///
14/// Use [`assert_that!`] to construct an `Assert` and then use methods to make some [`Assertion`].
15/// `Assertion`s can be returned from tests and verified by the test framework.
16#[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/// Information about an invocation of [`assert_that!`].
24#[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/// Construct the first part of an [`Assertion`].
48///
49/// ```no_run
50/// #[test]
51/// fn is_equal_to() -> impl Assertion {
52///     assert_that!(1 + 2).is_equal_to(3)
53/// }
54/// ```
55#[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    /// Don't use this constructor. [`assert_that!`] will fill in the `file`, `line` and `column`
70    /// fields for you and you should use it instead.
71    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    /// Converts from `&Assert<T>` to `Assert<&T>`.
79    pub const fn as_ref(&self) -> Assert<&T> {
80        Assert {
81            this: &self.this,
82            info: self.info,
83        }
84    }
85
86    /// Converts from `Assert<T>` or `&Assert<T>` to `Assert<&T::Target>` via [`Deref`].
87    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    /// Maps an `Assert<T>` to an `Assert<U>` by applying a function to its contained value.
98    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    /// Assert that `self` is equal to `other`.
106    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    /// Assert that `self` is not equal to `other`.
116    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    /// Assert that `self` is greater than `other`.
126    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    /// Assert that `self` is less than `other`.
136    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    /// Assert that `self` is greater than or equal to `other`.
146    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    /// Assert that `self` is less than or equal to `other`.
156    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    /// Assert that `self` is contained by `other`.
166    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    /// Assert that `self` is not contained by `other`.
174    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    /// Maps an `Assert<&T>` to `Assert<T>` by cloning the contained value.
184    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    /// Maps an `Assert<&T>` to `Assert<T>` by copying the contained value.
195    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    /// Assert that `self` is true.
208    pub const fn is_true(self) -> UnaryAssertion<bool> {
209        UnaryAssertion::new(self.this, self.this, "this.is_true()", self.info)
210    }
211
212    /// Assert that `self` is false.
213    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    /// Assert that `self` is `Some`.
220    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    /// Assert that `self` is `None`.
226    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    /// Assert that `self` is `Some` and the `Some` value matches a predicate.
232    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    /// Assert that `self` is `None` or the `Some` value matches a predicate.
245    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    /// Assert that `self` is `Ok`.
260    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    /// Assert that `self` is `Err`.
266    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    /// Assert that `self` is `Ok` and the `Ok` value matches a predicate.
272    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    /// Assert that `self` is `Err` and the `Err` value matches a predicate.
285    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    /// Assert that `self` is sorted according to [`PartialOrd::partial_cmp`].
300    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    /// Assert that `self` is not sorted according to [`PartialOrd::partial_cmp`].
309    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    /// Assert that `self` is sorted according to the provided comparison function.
318    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, // is this required? should I just use 'a and remove 'b?
322    {
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    /// Assert that `self` is not sorted according to the provided comparison function.
328    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, // is this required? should I just use 'a and remove 'b?
332    {
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    /// Assert that `self` is sorted according to the provided key extraction function.
338    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    /// Assert that `self` is not sorted according to the provided key extraction function.
349    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    /// Assert that `self` is empty.
365    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    /// Assert that `self` is not empty.
371    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    /// Assert that `self` has the given length.
377    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    /// Assert that `self` does not have the given length.
383    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    /// Assert that `self` contains `elem`.
389    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    /// Assert that `self` does not contain `elem`.
398    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}