use std::{error::Error, fmt};
#[inline]
pub fn handle_fatal<T, E: Error>(res: Result<T, E>) -> T {
handle_fatal_msg(res, "Fatal error encountered")
}
pub fn handle_fatal_msg<T, E: Error>(res: Result<T, E>, msg: &str) -> T {
match res {
Ok(o) => o,
Err(e) => {
tracing::error!(err = %Chain(&e), "{msg}");
std::process::exit(1);
},
}
}
#[derive(
Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Deserialize, serde::Serialize,
)]
#[repr(transparent)]
pub struct PrivateString(pub String);
impl std::fmt::Debug for PrivateString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("<private>") }
}
impl std::ops::Deref for PrivateString {
type Target = String;
#[inline]
fn deref(&self) -> &Self::Target { &self.0 }
}
impl std::ops::DerefMut for PrivateString {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl From<String> for PrivateString {
#[inline]
fn from(value: String) -> Self { Self(value) }
}
impl From<PrivateString> for String {
#[inline]
fn from(PrivateString(value): PrivateString) -> Self { value }
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct Chain<'a, E>(pub &'a E);
impl<E: Error> fmt::Display for Chain<'_, E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use fmt::Write;
enum IndentState {
NumberStart(usize), PlainStart, HangStart, MidLine, }
struct Indented<'a, F> {
f: &'a mut F,
state: IndentState,
}
impl<F: Write> Indented<'_, F> {
fn write_pad(&mut self) -> fmt::Result {
match std::mem::replace(&mut self.state, IndentState::MidLine) {
IndentState::NumberStart(i) => write!(self.f, "{i: >5}: "),
IndentState::PlainStart => write!(self.f, " "),
IndentState::HangStart => write!(self.f, " "),
IndentState::MidLine => Ok(()),
}
}
}
impl<F: Write> Write for Indented<'_, F> {
fn write_str(&mut self, mut s: &str) -> fmt::Result {
while let Some((head, tail)) = s.split_once('\n') {
if !head.is_empty() {
self.write_pad()?;
}
self.f.write_str(head)?;
self.f.write_char('\n')?;
self.state = IndentState::HangStart;
s = tail;
}
let trail = !s.is_empty();
if trail {
self.write_pad()?;
}
self.f.write_str(s)?;
self.state = if trail {
IndentState::MidLine
} else {
IndentState::HangStart
};
Ok(())
}
}
let Self(err) = *self;
write!(f, "{err}")?;
if let src @ Some(_) = err.source() {
let mut multi_src = false;
for (i, src) in std::iter::successors(src, |e| (*e).source()).enumerate() {
if i == 0 {
write!(f, "\nCaused by:")?;
multi_src = src.source().is_some();
}
writeln!(f)?;
write!(
Indented {
f,
state: if multi_src {
IndentState::NumberStart(i)
} else {
IndentState::PlainStart
},
},
"{src}"
)?;
}
}
Ok(())
}
}
pub mod stop {
use std::pin::Pin;
use tokio::sync::oneshot;
#[derive(Debug, Default)]
#[must_use = "Consider calling .as_unit()"]
#[allow(missing_copy_implementations)]
#[repr(transparent)]
pub struct StopCode(());
impl StopCode {
#[inline]
pub fn as_unit(self) { let Self(()) = self; }
}
#[derive(Debug)]
#[repr(transparent)]
pub struct StopTx(oneshot::Sender<StopCode>);
impl StopTx {
#[inline]
pub fn send_or_else<F: FnOnce()>(self, f: F) {
self.0.send(StopCode(())).unwrap_or_else(|StopCode(())| f());
}
#[inline]
pub fn maybe_send(self) { self.send_or_else(|| ()); }
}
#[derive(Debug)]
#[repr(transparent)]
pub struct StopRx(oneshot::Receiver<StopCode>);
impl StopRx {
pub fn as_unit(self) -> impl std::future::Future<Output = ()> + Send + Sync + 'static {
use futures_util::FutureExt;
self.0.map(|r| r.map_or((), |StopCode(())| ()))
}
}
impl std::future::Future for StopRx {
type Output = StopCode;
fn poll(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Self::Output> {
Pin::new(&mut self.get_mut().0)
.poll(cx)
.map(|r| r.unwrap_or(StopCode(())))
}
}
#[must_use]
pub fn channel() -> (StopTx, StopRx) {
let (tx, rx) = oneshot::channel();
(StopTx(tx), StopRx(rx))
}
}