use std::{
fmt::Debug,
future::Future,
panic::Location,
time::{Duration, UNIX_EPOCH},
};
pub trait LogHelper {
fn log(self) -> Self;
fn err_log(self, msg: &str) -> Self;
}
impl<R, E: Debug> LogHelper for Result<R, E> {
#[track_caller]
fn log(self) -> Self {
let msg = std::any::type_name::<E>().rsplit("::").next().unwrap();
match self {
Err(error) => {
let caller = Location::caller();
tracing::error!(?error, "Error({}:{}), {} ", caller.file(), caller.line(), msg);
Err(error)
}
ok => ok,
}
}
#[track_caller]
fn err_log(self, msg: &str) -> Self {
match self {
Err(error) => {
let caller = Location::caller();
tracing::error!(?error, "Error({}:{}), {} ", caller.file(), caller.line(), msg);
Err(error)
}
ok => ok,
}
}
}
impl<R> LogHelper for Option<R> {
#[track_caller]
fn log(self) -> Self {
let name = std::any::type_name::<R>().rsplit("::").next().unwrap();
self.err_log(name)
}
#[track_caller]
fn err_log(self, msg: &str) -> Self {
match self {
None => {
let caller = Location::caller();
tracing::error!("GotNone({}:{}), {} ", caller.file(), caller.line(), msg);
None
}
ok => ok,
}
}
}
pub trait PanicHelper {
type Success;
fn panic(self, msg: &str) -> Self::Success;
}
impl<R, E> PanicHelper for Result<R, E> {
type Success = R;
#[track_caller]
fn panic(self, msg: &str) -> R {
match self {
Ok(v) => v,
Err(_) => {
let caller = Location::caller();
tracing::error!("Panic({}:{}), {} ", caller.file(), caller.line(), msg);
panic!("Panic({}:{}), {} ", caller.file(), caller.line(), msg);
}
}
}
}
impl PanicHelper for bool {
type Success = ();
#[track_caller]
fn panic(self, msg: &str) {
match self {
true => (),
false => {
let caller = Location::caller();
tracing::error!("Panic({}:{}), {} ", caller.file(), caller.line(), msg);
panic!("Panic({}:{}), {} ", caller.file(), caller.line(), msg);
}
}
}
}
impl<R> PanicHelper for Option<R> {
type Success = R;
#[track_caller]
fn panic(self, msg: &str) -> R {
match self {
Some(v) => v,
None => {
let caller = Location::caller();
tracing::error!("Panic({}:{}), {} ", caller.file(), caller.line(), msg);
panic!("Panic({}:{}), {} ", caller.file(), caller.line(), msg);
}
}
}
}
pub trait TimeoutPanicHelper: Sized {
type Success;
#[track_caller]
fn timeout(self, time: Duration, msg: &str) -> impl Future<Output = Self::Success> {
let caller = Location::caller();
async move {
match self._timeout(caller, time, msg).await {
Ok(v) => v,
Err(msg) => {
tracing::error!("{}", msg);
panic!("{}", msg);
}
}
}
}
fn _timeout(
self,
caller: &'static Location<'static>,
time: Duration,
msg: &str,
) -> impl Future<Output = Result<Self::Success, String>>;
#[cfg(test)]
#[track_caller]
fn test_timeout(self) -> impl Future<Output = Self::Success> {
let caller = Location::caller();
async move {
match self._timeout(caller, Duration::from_millis(100), "test").await {
Ok(v) => v,
Err(msg) => {
tracing::error!("{}", msg);
panic!("{}", msg);
}
}
}
}
#[cfg(test)]
#[track_caller]
fn expect_timeout(self) -> impl Future<Output = ()> {
let caller = Location::caller();
async move {
if self._timeout(caller, Duration::from_millis(100), "").await.is_ok() {
let msg = format!("did not get expected timeout at 100ms, {}:{}", caller.file(), caller.line());
tracing::error!("{}", msg);
panic!("{}", msg);
}
}
}
}
impl<F: Future> TimeoutPanicHelper for F {
type Success = F::Output;
async fn _timeout(
self,
caller: &'static Location<'static>,
time: Duration,
msg: &str,
) -> Result<Self::Success, String> {
match tokio::time::timeout(time, self).await {
Ok(r) => Ok(r),
Err(_) => Err(format!("Timeout({:?}) Panic({}:{}), {} ", time, caller.file(), caller.line(), msg)),
}
}
}
pub fn now_ms() -> u64 {
std::time::SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
pub fn unknown_id_err(id: u16, name: &str) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::InvalidData, format!("unknown id for {}: {}", name, id))
}
pub fn unknown_version_err(version: u16, name: &str) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::InvalidData, format!("unknown version for {}: {}", name, version))
}