use std::panic::Location;
pub trait Must<T> {
#[track_caller]
fn must(self) -> T;
}
pub trait MustExpect<T>: Sized {
#[track_caller]
fn must_expect(self, msg: &str) -> T;
}
impl<T> Must<T> for Option<T> {
#[track_caller]
fn must(self) -> T {
self.unwrap_or_else(|| {
panic!("test invariant failed: expected Some, got None at {}", Location::caller())
})
}
}
impl<T> MustExpect<T> for Option<T> {
#[track_caller]
fn must_expect(self, msg: &str) -> T {
self.unwrap_or_else(|| {
panic!("{} (expected Some, got None) at {}", msg, Location::caller())
})
}
}
impl<T, E: std::fmt::Debug> Must<T> for Result<T, E> {
#[track_caller]
fn must(self) -> T {
self.unwrap_or_else(|err| {
panic!("test invariant failed: expected Ok, got {:?} at {}", err, Location::caller())
})
}
}
impl<T, E: std::fmt::Debug> MustExpect<T> for Result<T, E> {
#[track_caller]
fn must_expect(self, msg: &str) -> T {
self.unwrap_or_else(|err| {
panic!("{} (expected Ok, got {:?}) at {}", msg, err, Location::caller())
})
}
}
#[track_caller]
pub fn must_be_some<T>(opt: Option<T>) -> T {
opt.must()
}
#[track_caller]
pub fn must_be_ok<T, E: std::fmt::Debug>(res: Result<T, E>) -> T {
res.must()
}
#[track_caller]
pub fn must_expect_some<T>(opt: Option<T>, msg: &str) -> T {
opt.must_expect(msg)
}
#[track_caller]
pub fn must_expect_ok<T, E: std::fmt::Debug>(res: Result<T, E>, msg: &str) -> T {
res.must_expect(msg)
}
#[allow(clippy::disallowed_methods)]
#[track_caller]
pub fn must_eq<V: PartialEq + std::fmt::Debug>(left: V, right: V) {
assert_eq!(left, right);
}
#[track_caller]
pub fn must_ne<V: PartialEq + std::fmt::Debug>(left: V, right: V) {
assert_ne!(left, right);
}
#[track_caller]
pub fn must_be_true(b: bool) {
assert!(b,);
}
pub fn testcontainers_enabled() -> bool {
std::env::var("MAE_TESTCONTAINERS")
.map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn option_must_some_ok() {
let v = Some(42).must();
must_eq(v, 42);
}
#[test]
#[should_panic(expected = "expected Some, got None")]
fn option_must_none_panics() {
let _: i32 = None.must();
}
#[test]
fn option_must_expect_ok() {
let v = Some("hello").must_expect("should have value");
must_eq(v, "hello");
}
#[test]
#[should_panic(expected = "custom msg")]
fn option_must_expect_panics_with_msg() {
let _: i32 = None.must_expect("custom msg");
}
#[test]
fn result_must_ok() {
let v: i32 = Ok::<i32, &str>(7).must();
must_eq(v, 7);
}
#[test]
#[should_panic(expected = "expected Ok")]
fn result_must_err_panics() {
let r: Result<i32, &str> = Err("boom");
let _ = r.must();
}
#[test]
fn result_must_expect_ok() {
let v: &str = Ok::<&str, &str>("yes").must_expect("should succeed");
must_eq(v, "yes");
}
#[test]
#[should_panic(expected = "should fail")]
fn result_must_expect_panics_with_msg() {
let r: Result<i32, &str> = Err("nope");
let _ = r.must_expect("should fail");
}
#[test]
fn free_helpers_work() {
let a = must_be_some(Some(1));
let b = must_be_ok::<_, &str>(Ok::<i32, &str>(2));
let c = must_expect_some(Some(3), "msg");
let d = must_expect_ok::<_, &str>(Ok::<i32, &str>(4), "msg");
must_eq(a + b + c + d, 10);
}
#[test]
fn must_eq_and_ne() {
must_eq(5, 5);
must_ne(5, 6);
}
#[test]
fn must_be_true_works() {
must_be_true(true);
}
#[test]
#[should_panic]
fn must_be_true_panics_on_false() {
must_be_true(false);
}
}