Skip to main content

anvil_test/
expect.rs

1//! Fluent assertions — Pest's `expect()` translated to Rust.
2//!
3//! ```ignore
4//! use anvilforge::assay::*;
5//!
6//! expect(2 + 2).to_be(4);
7//! expect("hello world").to_contain("world");
8//! expect(vec![1, 2, 3]).to_have_length(3);
9//! expect(Some(5)).to_be_some();
10//! expect(value).not().to_be(0);
11//! ```
12//!
13//! Two top-level types: `Expect<T>` (positive form) and `Not<T>` (negated form,
14//! returned by `.not()`). Both implement the same set of matchers, but the
15//! `Not<T>` versions invert the assertion. Every matcher returns `self` so they
16//! can chain.
17
18use std::fmt::Debug;
19
20pub fn expect<T>(value: T) -> Expect<T> {
21    Expect(value)
22}
23
24pub struct Expect<T>(pub T);
25pub struct Not<T>(pub T);
26
27impl<T> Expect<T> {
28    pub fn not(self) -> Not<T> {
29        Not(self.0)
30    }
31    pub fn value(&self) -> &T {
32        &self.0
33    }
34    pub fn into_inner(self) -> T {
35        self.0
36    }
37}
38
39impl<T> Not<T> {
40    pub fn value(&self) -> &T {
41        &self.0
42    }
43}
44
45// ─── Equality matchers ─────────────────────────────────────────────────────
46
47impl<T: PartialEq + Debug> Expect<T> {
48    pub fn to_be(self, expected: T) -> Self {
49        assert!(
50            self.0 == expected,
51            "expected {:?} to equal {:?}",
52            self.0,
53            expected
54        );
55        self
56    }
57    pub fn to_equal(self, expected: T) -> Self {
58        self.to_be(expected)
59    }
60}
61
62impl<T: PartialEq + Debug> Not<T> {
63    pub fn to_be(self, expected: T) -> Self {
64        assert!(
65            self.0 != expected,
66            "expected {:?} NOT to equal {:?}",
67            self.0,
68            expected
69        );
70        self
71    }
72    pub fn to_equal(self, expected: T) -> Self {
73        self.to_be(expected)
74    }
75}
76
77// ─── Truthiness ────────────────────────────────────────────────────────────
78
79impl Expect<bool> {
80    pub fn to_be_true(self) -> Self {
81        assert!(self.0, "expected true, got false");
82        self
83    }
84    pub fn to_be_false(self) -> Self {
85        assert!(!self.0, "expected false, got true");
86        self
87    }
88}
89
90// ─── Ordering ──────────────────────────────────────────────────────────────
91
92impl<T: PartialOrd + Debug> Expect<T> {
93    pub fn to_be_greater_than(self, other: T) -> Self {
94        assert!(self.0 > other, "expected {:?} > {:?}", self.0, other);
95        self
96    }
97    pub fn to_be_less_than(self, other: T) -> Self {
98        assert!(self.0 < other, "expected {:?} < {:?}", self.0, other);
99        self
100    }
101    pub fn to_be_at_least(self, other: T) -> Self {
102        assert!(self.0 >= other, "expected {:?} >= {:?}", self.0, other);
103        self
104    }
105    pub fn to_be_at_most(self, other: T) -> Self {
106        assert!(self.0 <= other, "expected {:?} <= {:?}", self.0, other);
107        self
108    }
109}
110
111// ─── String contents ───────────────────────────────────────────────────────
112
113impl Expect<&str> {
114    pub fn to_contain(self, needle: &str) -> Self {
115        assert!(
116            self.0.contains(needle),
117            "expected `{}` to contain `{}`",
118            self.0,
119            needle
120        );
121        self
122    }
123    pub fn to_start_with(self, prefix: &str) -> Self {
124        assert!(
125            self.0.starts_with(prefix),
126            "expected `{}` to start with `{}`",
127            self.0,
128            prefix
129        );
130        self
131    }
132    pub fn to_end_with(self, suffix: &str) -> Self {
133        assert!(
134            self.0.ends_with(suffix),
135            "expected `{}` to end with `{}`",
136            self.0,
137            suffix
138        );
139        self
140    }
141    pub fn to_match(self, regex: &str) -> Self {
142        let re = regex::Regex::new(regex).expect("invalid regex");
143        assert!(
144            re.is_match(self.0),
145            "expected `{}` to match /{regex}/",
146            self.0
147        );
148        self
149    }
150}
151
152impl Expect<String> {
153    pub fn to_contain(self, needle: &str) -> Self {
154        assert!(
155            self.0.contains(needle),
156            "expected `{}` to contain `{needle}`",
157            self.0
158        );
159        self
160    }
161    pub fn to_start_with(self, prefix: &str) -> Self {
162        assert!(
163            self.0.starts_with(prefix),
164            "expected `{}` to start with `{prefix}`",
165            self.0
166        );
167        self
168    }
169    pub fn to_end_with(self, suffix: &str) -> Self {
170        assert!(
171            self.0.ends_with(suffix),
172            "expected `{}` to end with `{suffix}`",
173            self.0
174        );
175        self
176    }
177    pub fn to_match(self, regex: &str) -> Self {
178        let re = regex::Regex::new(regex).expect("invalid regex");
179        assert!(
180            re.is_match(&self.0),
181            "expected `{}` to match /{regex}/",
182            self.0
183        );
184        self
185    }
186}
187
188// ─── Containers ────────────────────────────────────────────────────────────
189
190impl<T: Debug + PartialEq> Expect<Vec<T>> {
191    pub fn to_have_length(self, len: usize) -> Self {
192        assert_eq!(
193            self.0.len(),
194            len,
195            "expected length {len}, got {}",
196            self.0.len()
197        );
198        self
199    }
200    pub fn to_contain(self, item: T) -> Self {
201        assert!(
202            self.0.iter().any(|v| v == &item),
203            "expected {:?} to contain {:?}",
204            self.0,
205            item
206        );
207        self
208    }
209    pub fn to_be_empty(self) -> Self {
210        assert!(self.0.is_empty(), "expected empty, got {:?}", self.0);
211        self
212    }
213}
214
215// ─── Option ────────────────────────────────────────────────────────────────
216
217impl<T: Debug> Expect<Option<T>> {
218    pub fn to_be_some(self) -> Self {
219        assert!(self.0.is_some(), "expected Some(_), got None");
220        self
221    }
222    pub fn to_be_none(self) -> Self {
223        assert!(self.0.is_none(), "expected None, got {:?}", self.0);
224        self
225    }
226}
227
228// ─── Result ────────────────────────────────────────────────────────────────
229
230impl<T: Debug, E: Debug> Expect<Result<T, E>> {
231    pub fn to_be_ok(self) -> Self {
232        assert!(self.0.is_ok(), "expected Ok, got {:?}", self.0);
233        self
234    }
235    pub fn to_be_err(self) -> Self {
236        assert!(self.0.is_err(), "expected Err, got {:?}", self.0);
237        self
238    }
239}