pub trait DebugWithContext<Ctx> {
fn fmt(&self, ctx: &Ctx, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
}
pub trait DebugWithContextExt<Ctx>: DebugWithContext<Ctx> {
fn dbg_ctx<'ctx, 't>(&'t self, ctx: &'ctx Ctx) -> ContextDebugger<'ctx, 't, Ctx, Self> {
ContextDebugger { ctx, value: self }
}
}
impl<T: ?Sized + DebugWithContext<Ctx>, Ctx> DebugWithContextExt<Ctx> for T {}
impl<'a, T: DebugWithContext<Ctx>, Ctx> DebugWithContext<Ctx> for &'a T {
fn fmt(&self, ctx: &Ctx, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
T::fmt(self, ctx, f)
}
}
pub struct ContextDebugger<'ctx, 't, C, T: ?Sized + DebugWithContext<C>> {
pub ctx: &'ctx C,
pub value: &'t T,
}
impl<C, T: ?Sized + DebugWithContext<C>> std::fmt::Debug for ContextDebugger<'_, '_, C, T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(self.ctx, f)
}
}
impl<C> DebugWithContext<C> for str {
fn fmt(&self, _: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self)
}
}
impl<C, T: DebugWithContext<C>> DebugWithContext<C> for Vec<T> {
fn fmt(&self, ctx: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut list = f.debug_list();
for elem in self.iter() {
list.entry(&ContextDebugger { ctx, value: elem });
}
list.finish()
}
}
impl<C, T: DebugWithContext<C>> DebugWithContext<C> for Option<T> {
fn fmt(&self, ctx: &C, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Some(val) => f.debug_tuple("Some").field(&val.dbg_ctx(ctx)).finish(),
None => f.debug_struct("None").finish(),
}
}
}