use std::ops::Deref;
use smallvec::SmallVec;
use tracing::Subscriber;
use tracing_subscriber::{layer::Context, registry::LookupSpan};
use super::{Ctx, Frame, StaticError};
#[derive(Debug, Default)]
pub(crate) struct ChainLink {
pub(crate) frame: Option<Frame>,
pub(crate) message: Option<String>,
}
#[derive(Debug, Default)]
pub(crate) struct Chain(SmallVec<[ChainLink; 16]>);
impl Chain {
pub(crate) fn new<S>(
tracing_context: &Context<'_, S>,
error: &StaticError,
) -> Self
where
S: Subscriber + for<'l> LookupSpan<'l>,
{
let mut chain = Self::default();
let mut current_error = Some(error);
while let Some(
error,
) = current_error {
let Some(
ctx,
) = error.downcast_ref::<Ctx>() else {
chain.0.push(ChainLink {
frame: None,
message: Some(error.to_string()),
});
current_error = error.source();
continue;
};
let span_ref = ctx.span.id().and_then(|id| tracing_context.span(&id));
let Some(
span_ref,
) = span_ref else {
let chain_link = ChainLink {
frame: None,
message: ctx.message.clone(),
};
chain.0.push(chain_link);
continue;
};
let frame = Frame::new(span_ref, &ctx.location);
chain.0.push(ChainLink {
frame,
message: Some(error.to_string()),
});
current_error = error.source();
}
chain
}
}
impl Deref for Chain {
type Target = SmallVec<[ChainLink; 16]>;
fn deref(
&self,
) -> &Self::Target {
&self.0
}
}