#![no_std]
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
use std::io::Write;
#[macro_export]
#[cfg(not(debug_assertions))]
macro_rules! safe_unwrap {
($reason:expr, $e:expr) => ($e.unwrap())
}
#[macro_export]
#[cfg(debug_assertions)]
macro_rules! safe_unwrap {
($reason:expr, $e:expr) => (
$e.expect(concat!("[BUG] violated: ",
$reason))
)
}
pub trait SafeUnwrap<T> {
fn safe_unwrap(self, msg: &str) -> T;
#[cfg(feature = "std")]
fn unwrap_or_abort(self, msg: &str) -> T;
#[cfg(feature = "std")]
fn unwrap_or_exit(self, msg: &str) -> T;
}
#[cfg(not(debug_assertions))]
impl<T, E: core::fmt::Debug> SafeUnwrap<T> for Result<T, E> {
#[inline]
fn safe_unwrap(self, _: &str) -> T {
self.unwrap()
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_abort(self, _: &str) -> T {
self.unwrap_or_else(|_| std::process::abort())
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_exit(self, _: &str) -> T {
self.unwrap_or_else(|_| std::process::exit(1))
}
}
#[cfg(not(debug_assertions))]
impl<T> SafeUnwrap<T> for Option<T> {
#[inline]
fn safe_unwrap(self, _: &str) -> T {
self.unwrap()
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_abort(self, _: &str) -> T {
self.unwrap_or_else(|| std::process::abort())
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_exit(self, _: &str) -> T {
self.unwrap_or_else(|| std::process::exit(1))
}
}
#[cfg(debug_assertions)]
impl<T, E: core::fmt::Debug> SafeUnwrap<T> for Result<T, E> {
#[inline]
fn safe_unwrap(self, msg: &str) -> T {
self.expect(msg)
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_abort(self, msg: &str) -> T {
self.unwrap_or_else(|_| {
let _ = writeln!(std::io::stderr(), "{}", msg);
std::process::abort()
})
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_exit(self, msg: &str) -> T {
self.unwrap_or_else(|_| {
let _ = writeln!(std::io::stderr(), "{}", msg);
std::process::exit(1)
})
}
}
#[cfg(debug_assertions)]
impl<T> SafeUnwrap<T> for Option<T> {
#[inline]
fn safe_unwrap(self, msg: &str) -> T {
self.expect(msg)
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_abort(self, msg: &str) -> T {
self.unwrap_or_else(|| {
let _ = writeln!(std::io::stderr(), "{}", msg);
std::process::abort()
})
}
#[cfg(feature = "std")]
#[inline]
fn unwrap_or_exit(self, msg: &str) -> T {
self.unwrap_or_else(|| {
let _ = writeln!(std::io::stderr(), "{}", msg);
std::process::exit(1)
})
}
}
#[cfg(test)]
mod tests {
use super::SafeUnwrap;
#[test]
fn works_when_ok() {
let x = safe_unwrap!("this comment is meaningless", Some(42));
assert_eq!(x, 42);
}
#[test]
#[should_panic]
fn doesnt_work_when_err() {
let _: Option<()> = safe_unwrap!("should fail", None);
}
#[test]
fn trait_works_when_ok() {
let x = Some(42).safe_unwrap("meaningless comment");
assert_eq!(x, 42);
let r: Result<usize, ()> = Ok(42);
let y = r.safe_unwrap("meaningless comment");
assert_eq!(y, 42);
}
#[test]
#[should_panic]
fn trait_doesnt_work_when_err() {
let _: Option<()> = None.safe_unwrap("should fail");
let _: Result<(), ()> = Err(()).safe_unwrap("should fail");
}
}