perl_test_must/lib.rs
1//! Safe unwrap replacements for tests.
2//!
3//! This crate provides panic-on-failure helpers that are safe to use in tests,
4//! avoiding explicit `unwrap()` calls which are denied by clippy policy.
5//!
6//! # Overview
7//!
8//! Three helpers cover the common cases:
9//! - [`must`] — extract the value from a `Result`, or panic with the error
10//! - [`must_some`] — extract the value from an `Option`, or panic
11//! - [`must_err`] — extract the error from a `Result`, or panic if `Ok`
12//!
13//! # Example
14//!
15//! ```rust
16//! use perl_test_must::{must, must_some, must_err};
17//!
18//! let result: Result<i32, &str> = Ok(42);
19//! assert_eq!(must(result), 42);
20//!
21//! let opt: Option<i32> = Some(7);
22//! assert_eq!(must_some(opt), 7);
23//!
24//! let err_result: Result<i32, &str> = Err("oops");
25//! assert_eq!(must_err(err_result), "oops");
26//! ```
27
28// This crate provides test helpers that intentionally panic on failure.
29// The must/must_some/must_err helpers are designed to panic in tests.
30#![allow(clippy::panic)]
31
32/// Extract the value from a `Result`, or panic with the error.
33///
34/// This is a test-only replacement for `unwrap` that is compliant
35/// with the "no unwrap/expect" policy.
36///
37/// Note: `#[must_use]` is intentionally omitted. `must()` is frequently
38/// called as an assertion (`must(fs::write(...))`) where the caller intentionally
39/// discards the `()` return value. Adding `#[must_use]` would trigger ~373
40/// spurious warnings across the workspace for those valid use cases.
41#[track_caller]
42pub fn must<T, E: std::fmt::Debug>(r: Result<T, E>) -> T {
43 match r {
44 Ok(v) => v,
45 Err(e) => panic!("unexpected Err<{}>: {e:?}", std::any::type_name::<E>()),
46 }
47}
48
49/// Extract the value from an `Option`, or panic.
50///
51/// This is a test-only replacement for `unwrap` that is compliant
52/// with the "no unwrap/expect" policy.
53#[track_caller]
54#[must_use]
55pub fn must_some<T>(o: Option<T>) -> T {
56 match o {
57 Some(v) => v,
58 None => panic!("unexpected None<{}>", std::any::type_name::<T>()),
59 }
60}
61
62/// Extract the error from a `Result`, or panic if `Ok`.
63///
64/// This is a test-only replacement for `.unwrap_err()` that is compliant
65/// with the "no unwrap/expect" policy.
66#[track_caller]
67#[must_use]
68pub fn must_err<T: std::fmt::Debug, E>(r: Result<T, E>) -> E {
69 match r {
70 Err(e) => e,
71 Ok(v) => panic!(
72 "expected Err<{}>, got Ok<{}>({v:?})",
73 std::any::type_name::<E>(),
74 std::any::type_name::<T>()
75 ),
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::{must, must_err, must_some};
82
83 #[test]
84 fn must_unwraps_ok() {
85 let result: Result<i32, &str> = Ok(42);
86 assert_eq!(must(result), 42);
87 }
88
89 #[test]
90 #[should_panic(expected = "unexpected Err")]
91 fn must_panics_on_err() {
92 let result: Result<i32, &str> = Err("oops");
93 must(result);
94 }
95
96 #[test]
97 fn must_some_unwraps_some() {
98 assert_eq!(must_some(Some(99)), 99);
99 }
100
101 #[test]
102 #[should_panic(expected = "unexpected None")]
103 fn must_some_panics_on_none() {
104 let _ = must_some(Option::<i32>::None);
105 }
106
107 #[test]
108 fn must_err_unwraps_err() {
109 let result: Result<i32, &str> = Err("expected error");
110 assert_eq!(must_err(result), "expected error");
111 }
112
113 #[test]
114 #[should_panic(expected = "expected Err")]
115 fn must_err_panics_on_ok() {
116 let result: Result<i32, &str> = Ok(1);
117 let _ = must_err(result);
118 }
119}