#![cfg_attr(not(doc), no_std)]
#![deny(
missing_docs,
elided_lifetimes_in_paths,
unsafe_code,
rustdoc::invalid_rust_codeblocks,
rustdoc::broken_intra_doc_links,
missing_copy_implementations,
unused_doc_comments
)]
#![cfg_attr(nightly_extra_checks, feature(rustdoc_missing_doc_code_examples))]
#![cfg_attr(nightly_extra_checks, forbid(rustdoc::missing_doc_code_examples))]
extern crate alloc;
use rootcause::{
Report, ReportMut, ReportRef, handlers,
markers::{self, Mutable, ReportOwnershipMarker, SendSync},
report_attachment::{ReportAttachment, ReportAttachmentMut, ReportAttachmentRef},
};
mod preformatted;
pub use preformatted::{PreformattedAttachment, PreformattedContext};
pub trait PreformatReportExt {
#[track_caller]
#[must_use]
fn preformat(&self) -> Report<PreformattedContext, Mutable, SendSync>;
}
pub trait PreformatAttachmentExt {
#[track_caller]
#[must_use]
fn preformat(&self) -> ReportAttachment<PreformattedAttachment, SendSync>;
}
impl<A: ?Sized, T> PreformatAttachmentExt for ReportAttachment<A, T> {
fn preformat(&self) -> ReportAttachment<PreformattedAttachment, SendSync> {
self.as_ref().preformat()
}
}
impl<'a, A: ?Sized> PreformatAttachmentExt for ReportAttachmentMut<'a, A> {
fn preformat(&self) -> ReportAttachment<PreformattedAttachment, SendSync> {
self.as_ref().preformat()
}
}
impl<'a, A: ?Sized> PreformatAttachmentExt for ReportAttachmentRef<'a, A> {
fn preformat(&self) -> ReportAttachment<PreformattedAttachment, SendSync> {
ReportAttachment::new_custom::<preformatted::PreformattedHandler>(
PreformattedAttachment::new_from_attachment(*self),
)
}
}
pub trait PreformatRootExt<C, T>: Sized {
#[track_caller]
#[must_use]
fn preformat_root(self) -> (C, Report<PreformattedContext, Mutable, T>)
where
PreformattedContext: markers::ObjectMarkerFor<T>;
}
pub trait ContextTransformNestedExt<C, T>: Sized {
type Output<D: 'static>;
#[track_caller]
#[must_use]
fn context_transform_nested<F, D>(self, f: F) -> Self::Output<D>
where
F: FnOnce(C) -> D,
D: markers::ObjectMarkerFor<T> + core::fmt::Display + core::fmt::Debug,
PreformattedContext: markers::ObjectMarkerFor<T>;
}
impl<C: ?Sized, O, T> PreformatReportExt for Report<C, O, T>
where
O: ReportOwnershipMarker,
{
fn preformat(&self) -> Report<PreformattedContext, Mutable, SendSync> {
self.as_ref().preformat()
}
}
impl<'a, C: ?Sized, T> PreformatReportExt for ReportMut<'a, C, T> {
fn preformat(&self) -> Report<PreformattedContext, Mutable, SendSync> {
self.as_ref().preformat()
}
}
impl<'a, C: ?Sized, O, T> PreformatReportExt for ReportRef<'a, C, O, T> {
fn preformat(&self) -> Report<PreformattedContext, Mutable, SendSync> {
let preformatted_context = PreformattedContext::new_from_context(*self);
Report::from_parts_unhooked::<preformatted::PreformattedHandler>(
preformatted_context,
self.children()
.iter()
.map(|sub_report| sub_report.preformat())
.collect(),
self.attachments()
.iter()
.map(|attachment| attachment.preformat().into_dynamic())
.collect(),
)
}
}
impl<C, T> PreformatRootExt<C, T> for Report<C, Mutable, T> {
fn preformat_root(self) -> (C, Report<PreformattedContext, Mutable, T>)
where
PreformattedContext: markers::ObjectMarkerFor<T>,
{
let preformatted = PreformattedContext::new_from_context(self.as_ref());
let (context, children, attachments) = self.into_parts();
(
context,
Report::from_parts_unhooked::<preformatted::PreformattedHandler>(
preformatted,
children,
attachments,
),
)
}
}
impl<C, T> ContextTransformNestedExt<C, T> for Report<C, Mutable, T> {
type Output<D: 'static> = Report<D, Mutable, T>;
fn context_transform_nested<F, D>(self, f: F) -> Report<D, Mutable, T>
where
F: FnOnce(C) -> D,
D: markers::ObjectMarkerFor<T> + core::fmt::Display + core::fmt::Debug,
PreformattedContext: markers::ObjectMarkerFor<T>,
{
let (context, report) = self.preformat_root();
report.context_custom::<handlers::Display, _>(f(context))
}
}
impl<V, C, T> ContextTransformNestedExt<C, T> for Result<V, Report<C, Mutable, T>> {
type Output<D: 'static> = Result<V, Report<D, Mutable, T>>;
fn context_transform_nested<F, D>(self, f: F) -> Result<V, Report<D, Mutable, T>>
where
F: FnOnce(C) -> D,
D: markers::ObjectMarkerFor<T> + core::fmt::Display + core::fmt::Debug,
PreformattedContext: markers::ObjectMarkerFor<T>,
{
match self {
Ok(value) => Ok(value),
Err(report) => {
let (context, report) = report.preformat_root();
Err(report.context_custom::<handlers::Display, _>(f(context)))
}
}
}
}
#[cfg(test)]
mod tests {
use alloc::format;
use core::any::TypeId;
use rootcause::{
ReportRef,
markers::{Local, Mutable, SendSync, Uncloneable},
prelude::*,
report_attachment::ReportAttachment,
};
use super::*;
#[derive(Debug)]
struct DemoError(u32);
impl core::fmt::Display for DemoError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "demo {}", self.0)
}
}
#[derive(Debug)]
struct Wrapper(#[allow(dead_code)] DemoError);
impl core::fmt::Display for Wrapper {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "wrapper")
}
}
#[test]
fn test_preformat() {
#[derive(Default)]
struct NonSendSyncError(core::cell::Cell<()>);
let non_send_sync_error = NonSendSyncError::default();
let report = report!(non_send_sync_error);
let report_ref: ReportRef<'_, NonSendSyncError, Uncloneable, Local> = report.as_ref();
let preformatted: Report<PreformattedContext, Mutable, SendSync> = report_ref.preformat();
assert_eq!(format!("{report}"), format!("{preformatted}"));
}
#[test]
fn test_preformat_root_extracts_typed_context() {
let report: Report<DemoError> = report!(DemoError(7)).attach("ctx-detail");
let display_before = format!("{report}");
let attachments_before = report.attachments().len();
let (context, preformatted) = report.preformat_root();
assert_eq!(context.0, 7);
assert_eq!(format!("{preformatted}"), display_before);
assert_eq!(
preformatted.current_context().original_type_id(),
TypeId::of::<DemoError>(),
);
assert_eq!(preformatted.attachments().len(), attachments_before);
}
#[test]
fn test_context_transform_nested_on_report() {
let inner: Report<DemoError> = report!(DemoError(3));
let outer: Report<Wrapper> = inner.context_transform_nested(Wrapper);
assert_eq!(outer.current_context_type_id(), TypeId::of::<Wrapper>());
assert_eq!(outer.iter_sub_reports().count(), 1);
let child = outer.children().get(0).unwrap();
assert_eq!(
child.current_context_type_id(),
TypeId::of::<PreformattedContext>(),
);
let child_typed = child
.downcast_report::<PreformattedContext>()
.expect("child should be PreformattedContext");
assert_eq!(
child_typed.current_context().original_type_id(),
TypeId::of::<DemoError>(),
);
}
#[test]
fn test_context_transform_nested_on_result_ok_passes_through() {
let ok: Result<i32, Report<DemoError>> = Ok(42);
let mapped: Result<i32, Report<Wrapper>> = ok.context_transform_nested(Wrapper);
assert_eq!(mapped.unwrap(), 42);
}
#[test]
fn test_context_transform_nested_on_result_err_wraps() {
let err: Result<i32, Report<DemoError>> = Err(report!(DemoError(9)));
let mapped: Result<i32, Report<Wrapper>> = err.context_transform_nested(Wrapper);
let outer = mapped.unwrap_err();
assert_eq!(outer.current_context_type_id(), TypeId::of::<Wrapper>());
assert_eq!(outer.iter_sub_reports().count(), 1);
let child = outer.children().get(0).unwrap();
let child_typed = child
.downcast_report::<PreformattedContext>()
.expect("child should be PreformattedContext");
assert_eq!(
child_typed.current_context().original_type_id(),
TypeId::of::<DemoError>(),
);
}
#[test]
fn test_preformat_attachment_owned_ref_mut() {
let mut attachment = ReportAttachment::new_sendsync(42u32);
let display = format!("{}", attachment.format_inner());
let debug = format!("{:?}", attachment.format_inner());
let from_owned = attachment.preformat();
let from_ref = attachment.as_ref().preformat();
let from_mut = attachment.as_mut().preformat();
for preformatted in [&from_owned, &from_ref, &from_mut] {
assert_eq!(format!("{}", preformatted.format_inner()), display);
assert_eq!(format!("{:?}", preformatted.format_inner()), debug);
assert_eq!(preformatted.inner().original_type_id(), TypeId::of::<u32>(),);
}
}
}