use alloc::{boxed::Box, fmt};
use core::{any::TypeId, marker::PhantomData};
use hashbrown::HashMap;
use rootcause_internals::handlers::{AttachmentFormattingStyle, FormattingFunction};
use crate::{
ReportRef,
hooks::{HookData, use_hooks},
markers::{Dynamic, Local, Uncloneable},
preformatted::PreformattedAttachment,
report_attachment::ReportAttachmentRef,
};
#[derive(Default)]
pub(crate) struct HookMap {
map: HashMap<TypeId, Box<dyn StoredHook>, rustc_hash::FxBuildHasher>,
}
impl core::fmt::Debug for HookMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.map.values().fmt(f)
}
}
impl HookMap {
fn get(&self, type_id: TypeId) -> Option<&dyn StoredHook> {
Some(&**self.map.get(&type_id)?)
}
pub(crate) fn insert<A, H>(&mut self, hook: H)
where
A: Sized + 'static,
H: AttachmentFormatterHook<A>,
{
let hook: Hook<A, H> = Hook {
hook,
_hooked_type: PhantomData,
};
let hook: Box<Hook<A, H>> = Box::new(hook);
self.map.insert(TypeId::of::<A>(), hook);
}
}
struct Hook<A, H>
where
A: 'static,
{
hook: H,
_hooked_type: PhantomData<fn(A) -> A>,
}
impl<A, H> core::fmt::Debug for Hook<A, H>
where
A: 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"AttachmentFormattingHook<{}, {}>",
core::any::type_name::<A>(),
core::any::type_name::<H>(),
)
}
}
#[derive(Copy, Clone, Debug)]
pub struct AttachmentParent<'a> {
pub report: ReportRef<'a, Dynamic, Uncloneable, Local>,
pub attachment_index: usize,
}
trait StoredHook: 'static + Send + Sync + core::fmt::Debug {
unsafe fn display(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result;
unsafe fn debug(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result;
fn display_preformatted(
&self,
attachment: ReportAttachmentRef<'_, PreformattedAttachment>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result;
fn debug_preformatted(
&self,
attachment: ReportAttachmentRef<'_, PreformattedAttachment>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result;
fn preferred_formatting_style(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle;
}
pub trait AttachmentFormatterHook<A>: 'static + Send + Sync {
fn display(
&self,
attachment: ReportAttachmentRef<'_, A>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let _ = attachment_parent;
fmt::Display::fmt(&attachment.format_inner_unhooked(), formatter)
}
fn display_preformatted(
&self,
attachment: ReportAttachmentRef<'_, PreformattedAttachment>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let _ = attachment_parent;
fmt::Display::fmt(&attachment.format_inner_unhooked(), formatter)
}
fn debug(
&self,
attachment: ReportAttachmentRef<'_, A>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let _ = attachment_parent;
fmt::Debug::fmt(&attachment.format_inner_unhooked(), formatter)
}
fn debug_preformatted(
&self,
attachment: ReportAttachmentRef<'_, PreformattedAttachment>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let _ = attachment_parent;
fmt::Debug::fmt(&attachment.format_inner_unhooked(), formatter)
}
fn preferred_formatting_style(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
attachment.preferred_formatting_style_unhooked(report_formatting_function)
}
}
impl<A, H> StoredHook for Hook<A, H>
where
H: AttachmentFormatterHook<A>,
{
unsafe fn display(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let attachment = unsafe { attachment.downcast_attachment_unchecked::<A>() };
self.hook.display(attachment, attachment_parent, formatter)
}
unsafe fn debug(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let attachment = unsafe { attachment.downcast_attachment_unchecked::<A>() };
self.hook.debug(attachment, attachment_parent, formatter)
}
fn display_preformatted(
&self,
attachment: ReportAttachmentRef<'_, PreformattedAttachment>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
self.hook
.display_preformatted(attachment, attachment_parent, formatter)
}
fn debug_preformatted(
&self,
attachment: ReportAttachmentRef<'_, PreformattedAttachment>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
self.hook
.debug_preformatted(attachment, attachment_parent, formatter)
}
fn preferred_formatting_style(
&self,
attachment: ReportAttachmentRef<'_, Dynamic>,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
self.hook
.preferred_formatting_style(attachment, report_formatting_function)
}
}
pub(crate) fn display_attachment(
attachment: ReportAttachmentRef<'_, Dynamic>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
use_hooks(|hook_data: Option<&HookData>| {
if let Some(hook_data) = hook_data {
let attachment_formatters: &HookMap = &hook_data.attachment_formatters;
if let Some(attachment) = attachment.downcast_attachment::<PreformattedAttachment>()
&& let Some(hook) = attachment_formatters.get(attachment.inner().original_type_id())
{
return hook.display_preformatted(attachment, attachment_parent, formatter);
}
if let Some(hook) = attachment_formatters.get(attachment.inner_type_id()) {
unsafe {
return hook.display(attachment, attachment_parent, formatter);
}
}
}
fmt::Display::fmt(&attachment.format_inner_unhooked(), formatter)
})
}
pub(crate) fn debug_attachment(
attachment: ReportAttachmentRef<'_, Dynamic>,
attachment_parent: Option<AttachmentParent<'_>>,
formatter: &mut fmt::Formatter<'_>,
) -> fmt::Result {
use_hooks(|hook_data: Option<&HookData>| {
if let Some(hook_data) = hook_data {
let attachment_formatters: &HookMap = &hook_data.attachment_formatters;
if let Some(attachment) = attachment.downcast_attachment::<PreformattedAttachment>()
&& let Some(hook) = attachment_formatters.get(attachment.inner().original_type_id())
{
return hook.debug_preformatted(attachment, attachment_parent, formatter);
}
if let Some(hook) = attachment_formatters.get(attachment.inner_type_id()) {
unsafe {
return hook.debug(attachment, attachment_parent, formatter);
}
}
}
fmt::Debug::fmt(&attachment.format_inner_unhooked(), formatter)
})
}
pub(crate) fn get_preferred_formatting_style(
attachment: ReportAttachmentRef<'_, Dynamic>,
report_formatting_function: FormattingFunction,
) -> AttachmentFormattingStyle {
use_hooks(|hook_data: Option<&HookData>| {
if let Some(hook_data) = hook_data {
let attachment_formatters: &HookMap = &hook_data.attachment_formatters;
if let Some(inner) = attachment.downcast_inner::<PreformattedAttachment>()
&& let Some(hook) = attachment_formatters.get(inner.original_type_id())
{
return hook.preferred_formatting_style(attachment, report_formatting_function);
}
if let Some(hook) = attachment_formatters.get(attachment.inner_type_id()) {
return hook.preferred_formatting_style(attachment, report_formatting_function);
}
}
attachment.preferred_formatting_style_unhooked(report_formatting_function)
})
}