1use std::fmt::Debug;
6
7std::thread_local! {
8 pub static CURRENT_TEST_NAME: std::cell::RefCell<Option<String>> = const { std::cell::RefCell::new(None) };
10}
11
12pub fn set_current_test_name(name: Option<String>) {
14 CURRENT_TEST_NAME.with(|cell| {
15 *cell.borrow_mut() = name;
16 });
17}
18
19fn get_test_name() -> Option<String> {
21 CURRENT_TEST_NAME.with(|cell| cell.borrow().clone())
22}
23
24fn format_header(location: &str) -> String {
26 if let Some(name) = get_test_name() {
27 format!("\nTest: \"{name}\"\n at {location}\n")
28 } else {
29 format!("\nassertion failed at {location}\n")
30 }
31}
32
33pub struct Expect<T> {
35 value: T,
36 location: &'static str,
37}
38
39impl<T> Expect<T> {
40 pub fn new(value: T, location: &'static str) -> Self {
42 Self { value, location }
43 }
44}
45
46impl<T: Debug + PartialEq> Expect<T> {
48 pub fn to_equal(&self, expected: T) {
55 if self.value != expected {
56 panic!(
57 "{}\n expect!(actual).to_equal(expected)\n\n Expected: {:?}\n Received: {:?}\n",
58 format_header(self.location),
59 expected,
60 self.value
61 );
62 }
63 }
64
65 pub fn to_not_equal(&self, unexpected: T) {
72 if self.value == unexpected {
73 panic!(
74 "{}\n expect!(actual).to_not_equal(value)\n\n Expected NOT: {:?}\n Received: {:?}\n",
75 format_header(self.location),
76 unexpected,
77 self.value
78 );
79 }
80 }
81}
82
83impl Expect<bool> {
85 pub fn to_be_true(&self) {
92 if !self.value {
93 panic!(
94 "{}\n expect!(value).to_be_true()\n\n Expected: true\n Received: false\n",
95 format_header(self.location)
96 );
97 }
98 }
99
100 pub fn to_be_false(&self) {
107 if self.value {
108 panic!(
109 "{}\n expect!(value).to_be_false()\n\n Expected: false\n Received: true\n",
110 format_header(self.location)
111 );
112 }
113 }
114}
115
116impl<T: Debug> Expect<Option<T>> {
118 pub fn to_be_some(&self) {
125 if self.value.is_none() {
126 panic!(
127 "{}\n expect!(option).to_be_some()\n\n Expected: Some(_)\n Received: None\n",
128 format_header(self.location)
129 );
130 }
131 }
132
133 pub fn to_be_none(&self) {
140 if let Some(ref v) = self.value {
141 panic!(
142 "{}\n expect!(option).to_be_none()\n\n Expected: None\n Received: Some({:?})\n",
143 format_header(self.location),
144 v
145 );
146 }
147 }
148}
149
150impl<T: Debug + PartialEq> Expect<Option<T>> {
152 pub fn to_contain_value(&self, expected: T) {
159 match &self.value {
160 Some(v) if *v == expected => {}
161 Some(v) => {
162 panic!(
163 "{}\n expect!(option).to_contain_value(expected)\n\n Expected: Some({:?})\n Received: Some({:?})\n",
164 format_header(self.location),
165 expected,
166 v
167 );
168 }
169 None => {
170 panic!(
171 "{}\n expect!(option).to_contain_value(expected)\n\n Expected: Some({:?})\n Received: None\n",
172 format_header(self.location),
173 expected
174 );
175 }
176 }
177 }
178}
179
180impl<T: Debug, E: Debug> Expect<Result<T, E>> {
182 pub fn to_be_ok(&self) {
189 if let Err(ref e) = self.value {
190 panic!(
191 "{}\n expect!(result).to_be_ok()\n\n Expected: Ok(_)\n Received: Err({:?})\n",
192 format_header(self.location),
193 e
194 );
195 }
196 }
197
198 pub fn to_be_err(&self) {
205 if let Ok(ref v) = self.value {
206 panic!(
207 "{}\n expect!(result).to_be_err()\n\n Expected: Err(_)\n Received: Ok({:?})\n",
208 format_header(self.location),
209 v
210 );
211 }
212 }
213}
214
215impl Expect<String> {
217 pub fn to_contain(&self, substring: &str) {
224 if !self.value.contains(substring) {
225 panic!(
226 "{}\n expect!(string).to_contain(substring)\n\n Expected to contain: {:?}\n Received: {:?}\n",
227 format_header(self.location),
228 substring,
229 self.value
230 );
231 }
232 }
233
234 pub fn to_start_with(&self, prefix: &str) {
241 if !self.value.starts_with(prefix) {
242 panic!(
243 "{}\n expect!(string).to_start_with(prefix)\n\n Expected to start with: {:?}\n Received: {:?}\n",
244 format_header(self.location),
245 prefix,
246 self.value
247 );
248 }
249 }
250
251 pub fn to_end_with(&self, suffix: &str) {
258 if !self.value.ends_with(suffix) {
259 panic!(
260 "{}\n expect!(string).to_end_with(suffix)\n\n Expected to end with: {:?}\n Received: {:?}\n",
261 format_header(self.location),
262 suffix,
263 self.value
264 );
265 }
266 }
267
268 pub fn to_have_length(&self, expected: usize) {
275 let actual = self.value.len();
276 if actual != expected {
277 panic!(
278 "{}\n expect!(string).to_have_length({})\n\n Expected length: {}\n Actual length: {}\n Value: {:?}\n",
279 format_header(self.location),
280 expected,
281 expected,
282 actual,
283 self.value
284 );
285 }
286 }
287
288 pub fn to_be_empty(&self) {
295 if !self.value.is_empty() {
296 panic!(
297 "{}\n expect!(string).to_be_empty()\n\n Expected: \"\"\n Received: {:?}\n",
298 format_header(self.location),
299 self.value
300 );
301 }
302 }
303}
304
305impl Expect<&str> {
307 pub fn to_contain(&self, substring: &str) {
309 if !self.value.contains(substring) {
310 panic!(
311 "{}\n expect!(string).to_contain(substring)\n\n Expected to contain: {:?}\n Received: {:?}\n",
312 format_header(self.location),
313 substring,
314 self.value
315 );
316 }
317 }
318
319 pub fn to_start_with(&self, prefix: &str) {
321 if !self.value.starts_with(prefix) {
322 panic!(
323 "{}\n expect!(string).to_start_with(prefix)\n\n Expected to start with: {:?}\n Received: {:?}\n",
324 format_header(self.location),
325 prefix,
326 self.value
327 );
328 }
329 }
330
331 pub fn to_end_with(&self, suffix: &str) {
333 if !self.value.ends_with(suffix) {
334 panic!(
335 "{}\n expect!(string).to_end_with(suffix)\n\n Expected to end with: {:?}\n Received: {:?}\n",
336 format_header(self.location),
337 suffix,
338 self.value
339 );
340 }
341 }
342
343 pub fn to_have_length(&self, expected: usize) {
345 let actual = self.value.len();
346 if actual != expected {
347 panic!(
348 "{}\n expect!(string).to_have_length({})\n\n Expected length: {}\n Actual length: {}\n Value: {:?}\n",
349 format_header(self.location),
350 expected,
351 expected,
352 actual,
353 self.value
354 );
355 }
356 }
357
358 pub fn to_be_empty(&self) {
360 if !self.value.is_empty() {
361 panic!(
362 "{}\n expect!(string).to_be_empty()\n\n Expected: \"\"\n Received: {:?}\n",
363 format_header(self.location),
364 self.value
365 );
366 }
367 }
368}
369
370impl<T: Debug + PartialEq> Expect<Vec<T>> {
372 pub fn to_have_length(&self, expected: usize) {
379 let actual = self.value.len();
380 if actual != expected {
381 panic!(
382 "{}\n expect!(vec).to_have_length({})\n\n Expected length: {}\n Actual length: {}\n",
383 format_header(self.location),
384 expected,
385 expected,
386 actual
387 );
388 }
389 }
390
391 pub fn to_contain(&self, item: &T) {
398 if !self.value.contains(item) {
399 panic!(
400 "{}\n expect!(vec).to_contain(item)\n\n Expected to contain: {:?}\n Received: {:?}\n",
401 format_header(self.location),
402 item,
403 self.value
404 );
405 }
406 }
407
408 pub fn to_be_empty(&self) {
415 if !self.value.is_empty() {
416 panic!(
417 "{}\n expect!(vec).to_be_empty()\n\n Expected: []\n Received: {:?}\n",
418 format_header(self.location),
419 self.value
420 );
421 }
422 }
423}
424
425#[allow(clippy::neg_cmp_op_on_partial_ord)]
427impl<T: Debug + PartialOrd> Expect<T> {
428 pub fn to_be_greater_than(&self, expected: T) {
435 if !(self.value > expected) {
436 panic!(
437 "{}\n expect!(value).to_be_greater_than(expected)\n\n Expected: > {:?}\n Received: {:?}\n",
438 format_header(self.location),
439 expected,
440 self.value
441 );
442 }
443 }
444
445 pub fn to_be_less_than(&self, expected: T) {
452 if !(self.value < expected) {
453 panic!(
454 "{}\n expect!(value).to_be_less_than(expected)\n\n Expected: < {:?}\n Received: {:?}\n",
455 format_header(self.location),
456 expected,
457 self.value
458 );
459 }
460 }
461
462 pub fn to_be_greater_than_or_equal(&self, expected: T) {
469 if !(self.value >= expected) {
470 panic!(
471 "{}\n expect!(value).to_be_greater_than_or_equal(expected)\n\n Expected: >= {:?}\n Received: {:?}\n",
472 format_header(self.location),
473 expected,
474 self.value
475 );
476 }
477 }
478
479 pub fn to_be_less_than_or_equal(&self, expected: T) {
486 if !(self.value <= expected) {
487 panic!(
488 "{}\n expect!(value).to_be_less_than_or_equal(expected)\n\n Expected: <= {:?}\n Received: {:?}\n",
489 format_header(self.location),
490 expected,
491 self.value
492 );
493 }
494 }
495}