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