use std::{error::Error, fmt::Debug}; use log::{debug, error}; // So we still get a useful panic when logs are filtered macro_rules! error_and_panic { ($($arg:tt)*) => { log::error!($($arg)*); panic!($($arg)*); }; } pub trait UnwrapLog { type Target; fn unwrap_or_log(self, default: Self::Target) -> Self::Target; fn unwrap_log(self) -> Self::Target; fn expect_log(self, expect: &str) -> Self::Target; } impl UnwrapLog for Result where T: Debug, E: Error, { type Target = T; fn unwrap_or_log(self, default: Self::Target) -> Self::Target { match self { Ok(val) => val, Err(err) => { error!( "[{}] unwrap_or_log got error: {err}", std::any::type_name::() ); debug!("^ defaulting to {default:#?}"); default } } } fn unwrap_log(self) -> Self::Target { match self { Ok(target) => target, Err(err) => { error_and_panic!("called `Result::unwrap_log()` on an `Err` value: {err}"); } } } fn expect_log(self, expect: &str) -> Self::Target { match self { Ok(target) => target, Err(err) => { error_and_panic!( "[{expect}] called `Result::expect_log()` on an `Err` value: {err}" ); } } } } impl UnwrapLog for Option where T: Debug, { type Target = T; fn unwrap_or_log(self, default: Self::Target) -> Self::Target { match self { Some(v) => v, None => { error!( "[{}] unwrap_or_log was None", std::any::type_name::() ); debug!("^ defaulting to {default:#?}"); default } } } fn unwrap_log(self) -> Self::Target { match self { Some(target) => target, None => { error_and_panic!("called `Option::unwrap_log()` on a `None` value"); } } } fn expect_log(self, expect: &str) -> Self::Target { match self { Some(target) => target, None => { error_and_panic!("[{expect}] called `Option::expect_log()` on an `None` value"); } } } }