Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .envrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
use flake
use flake
2 changes: 1 addition & 1 deletion .github/instructions/rootcause.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Different visibility levels require different levels of documentation detail:
````rust
/// Creates a report with the given context.
///
/// This is the primary way to create reports. Use the `report!()` macro
/// This is the primary way to create reports. Use the [`report!()`](crate::report) macro
/// for more convenient report creation with format string support.
///
/// # Examples
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
/.direnv
/.claude
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
"cSpell.userWords": [],
"cSpell.flagWords": [],
"cSpell.ignoreWords": [],
"rust-analyzer.cargo.features": "all"
}
12 changes: 8 additions & 4 deletions examples/anyhow_interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,17 @@
//! # Conversion Overview
//!
//! ## From Anyhow to Rootcause
//! - `.into_rootcause()` - Convert `anyhow::Result<T>` or `anyhow::Error`
//! - [`.into_rootcause()`] - Convert [`anyhow::Result<T>`] or [`anyhow::Error`]
//!
//! ## From Rootcause to Anyhow
//! - `.into_anyhow()` - Convert `Result<T, Report>` or `Report`
//! - `.into()` - Use `From<Report>` for automatic conversion
//! - `?` operator - Automatically converts `Report` to `anyhow::Error` in
//! - [`.into_anyhow()`] - Convert [`Result<T, Report>`] or [`Report`]
//! - [`.into()`](Into::into) - Use [`From<Report>`] for automatic conversion
//! - `?` operator - Automatically converts [`Report`] to [`anyhow::Error`] in
//! anyhow functions
//!
//! [`.into_rootcause()`]: rootcause::compat::IntoRootcause::into_rootcause
//! [`.attach()`]: rootcause::Report::attach
//! [`.into_anyhow()`]: rootcause::compat::anyhow1::IntoAnyhow::into_anyhow

// Import only what we need to avoid conflicting with anyhow's Context trait
use rootcause::{
Expand Down
4 changes: 2 additions & 2 deletions examples/anyhow_migration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
//! you control all the code but want to be cautious about breaking changes.
//!
//! The example treats each component as if it came from a separate crate:
//! - `metrics`: A metrics collection library (pretend you depend on this)
//! - `kv_store`: A key-value store (pretend you depend on this too)
//! - [`metrics`](v1_original_anyhow::metrics): A metrics collection library (pretend you depend on this)
//! - [`kv_store`](v1_original_anyhow::kv_store): A key-value store (pretend you depend on this too)
//! - Your application: The code you're migrating
//!
//! # Migration Stages
Expand Down
4 changes: 2 additions & 2 deletions examples/anyhow_migration/stage2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
//! features like `.attach()` without touching any dependencies.
//!
//! **What changed:**
//! - Application's `main` module now returns `Result<(), Report>`
//! - Use `.into_rootcause()` when calling dependency methods
//! - Application's [`main`] module now returns [`Result<(), Report>`]
//! - Use [`.into_rootcause()`](crate""") when calling dependency methods
//! - Can now use `.attach()`, rich context, and other rootcause features
//!
//! **What stayed the same:**
Expand Down
12 changes: 8 additions & 4 deletions examples/anyhow_migration/stage3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
//! users.
//!
//! **What changed:**
//! - Internal methods like `flush_internal()` now use rootcause
//! - Can add rich context (`.attach()`) in internal implementation
//! - Use `.into_anyhow()` to convert back when returning from trait methods
//! - Internal methods like [`.flush_internal()`] now use rootcause
//! - Can add rich context ([`.attach()`]) in internal implementation
//! - Use [`.into_anyhow()`] to convert back when returning from trait methods
//!
//! [`.flush_internal()`]: metrics::MetricsCollector::flush_internal
//! [`.attach()`]: rootcause::Report::attach
//! [`.into_anyhow()`]: rootcause::compat::anyhow1::IntoAnyhow::into_anyhow
//!
//! **What stayed the same:**
//! - Public `Metrics` trait still uses `anyhow::Result` (no breaking change)
//! - Public [`Metrics`](metrics::Metrics) trait still uses [`anyhow::Result`] (no breaking change)
//! - KV store and application unchanged from stage 2

// -------------------------------------------------------------------------
Expand Down
12 changes: 8 additions & 4 deletions examples/anyhow_migration/stage4.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
//! **What changed:**
//! - KV store methods split into public (anyhow) and internal (rootcause)
//! versions
//! - Use `.into_rootcause()` when calling metrics (still anyhow)
//! - Use `.into_anyhow()` when returning from public methods
//! - Rich error context with `.attach()` in internal methods
//! - Use [`.into_rootcause()`] when calling metrics (still anyhow)
//! - Use [`.into_anyhow()`] when returning from public methods
//! - Rich error context with [`.attach()`] in internal methods
//!
//! [`.into_rootcause()`]: rootcause::compat::IntoRootcause::into_rootcause
//! [`.attach()`]: rootcause::Report::attach
//! [`.into_anyhow()`]: rootcause::compat::anyhow1::IntoAnyhow::into_anyhow
//!
//! **What stayed the same:**
//! - Public KV store API still uses `anyhow::Result` (no breaking change)
//! - Public KV store API still uses [`anyhow::Result`] (no breaking change)
//! - Metrics and application unchanged from stage 3
pub mod metrics {
use std::collections::HashMap;
Expand Down
7 changes: 5 additions & 2 deletions examples/anyhow_migration/stage5.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
//! This stage shows the final, clean state with no conversions needed.
//!
//! **What changed:**
//! - `Metrics` trait now uses `Result<(), Report>` (**breaking change**)
//! - All `.into_rootcause()` and `.into_anyhow()` calls removed
//! - [`Metrics`](metrics::Metrics) trait now uses [`Result<(), Report>`] (**breaking change**)
//! - All [`.into_rootcause()`] and [`.into_anyhow()`] calls removed
//! - Clean, consistent rootcause usage throughout
//!
//! [`.into_rootcause()`]: rootcause::compat::IntoRootcause::into_rootcause
//! [`.into_anyhow()`]: rootcause::compat::anyhow1::IntoAnyhow::into_anyhow
//!
//! **Benefits:**
//! - No conversion overhead
//! - Consistent error handling patterns across all crates
Expand Down
8 changes: 4 additions & 4 deletions examples/boxed_error_interop.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Quick reference for bidirectional boxed error interoperability.
//!
//! This example demonstrates all the ways to convert between rootcause
//! [`Report`]s and boxed error trait objects (`Box<dyn Error>`). This is
//! [`Report`]s and boxed error trait objects ([`Box<dyn Error>`]). This is
//! useful for integrating with APIs that expect standard Rust error types.
//!
//! # Conversion Overview
Expand All @@ -11,9 +11,9 @@
//! Error>`
//!
//! ## From Rootcause to Boxed Errors
//! - `.into_boxed_error()` - Convert `Result<T, Report>` or `Report`
//! - `.into()` - Use `From<Report>` for automatic conversion
//! - `?` operator - Automatically converts `Report` to `Box<dyn Error>` in
//! - [`.into_boxed_error()`] - Convert [`Result<T, Report>`] or [`Report`]
//! - [`.into()`](Into::into) - Use [`From<Report>`] for automatic conversion
//! - `?` operator - Automatically converts [`Report`] to [`Box<dyn Error>`] in
//! functions returning boxed errors
//!
//! # Thread Safety
Expand Down
13 changes: 9 additions & 4 deletions examples/context_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@
//!
//! This example compares four methods for transforming report contexts:
//!
//! - `context()`: Wraps report as child under new context
//! - `context_to()`: Uses `ReportConversion` trait implementation
//! - `context_transform()`: Changes context type in-place
//! - `context_transform_nested()`: Preformats and wraps as child
//! - [`.context()`]: Wraps report as child under new context
//! - [`.context_to()`]: Uses [`ReportConversion`] trait implementation
//! - [`.context_transform()`]: Changes context type in-place
//! - [`.context_transform_nested()`]: Preformats and wraps as child
//!
//! [`.context()`]: Report::context
//! [`.context_to()`]: Report::context_to
//! [`.context_transform()`]: Report::context_transform
//! [`.context_transform_nested()`]: Report::context_transform_nested
//!
//! The focus is on understanding **what each method does to the report
//! structure** and **what information is preserved or lost**.
Expand Down
4 changes: 2 additions & 2 deletions rootcause-backtrace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@ impl FramePath {
/// Extension trait for attaching backtraces to reports.
///
/// This trait provides methods to easily attach a captured backtrace to a
/// report or to the error contained within a `Result`.
/// report or to the error contained within a [`Result`].
///
/// # Examples
///
Expand All @@ -928,7 +928,7 @@ impl FramePath {
/// let report = report!(io::Error::other("An error occurred")).attach_backtrace();
/// ```
///
/// Attach backtrace to a `Result`:
/// Attach backtrace to a [`Result`]:
///
/// ```
/// use std::io;
Expand Down
2 changes: 1 addition & 1 deletion rootcause-internals/src/attachment/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub(crate) struct AttachmentData<A: 'static> {
/// The following safety invariants are guaranteed to be upheld as long as
/// this struct exists:
///
/// 1. The vtable must always point to an `AttachmentVtable` created for the
/// 1. The vtable must always point to an [`AttachmentVtable`] created for the
/// actual attachment type `A` stored below. This is true even when
/// accessed via type-erased pointers.
vtable: &'static AttachmentVtable,
Expand Down
31 changes: 15 additions & 16 deletions rootcause-internals/src/attachment/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ pub struct RawAttachment {
/// this struct exists:
///
/// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
/// for some `A` using `Box::into_raw`.
/// 2. The pointer will point to the same `AttachmentData<A>` for the entire
/// lifetime of this object.
/// for some `A` using [`Box::into_raw`].
/// 2. The pointer will point to the same [`AttachmentData<A>`](AttachmentData)
/// for the entire lifetime of this object.
/// 3. The pointee is properly initialized for the entire lifetime of this
/// object, except during the execution of the `Drop` implementation.
/// 4. The pointer is the sole owner of the `AttachmentData` instance.
/// object, except during the execution of the [`Drop`] implementation.
/// 4. The pointer is the sole owner of the [`AttachmentData`] instance.
ptr: NonNull<AttachmentData<Erased>>,
}

Expand Down Expand Up @@ -161,13 +161,12 @@ pub struct RawAttachmentRef<'a> {
/// The following safety invariants are guaranteed to be upheld as long as
/// this struct exists:
///
/// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
/// for some `A` using `Box::into_raw`.
/// 2. The pointer will point to the same `AttachmentData<A>` for the entire
/// 1. The pointer must have been created from a [`Box<AttachmentData<A>>`]
/// for some `A` using [`Box::into_raw`].
/// 2. The pointer will point to the same [`AttachmentData<A>`](AttachmentData) for the entire
/// lifetime of this object.
/// 3. This pointer represents read-only access to the `AttachmentData` for
/// the lifetime `'a` with the same semantics as a `&'a
/// AttachmentData<C>`.
/// 3. This pointer represents read-only access to the [`AttachmentData`] for
/// the lifetime `'a` with the same semantics as a [`&'a AttachmentData<C>`](AttachmentData).
ptr: NonNull<AttachmentData<Erased>>,

/// Marker to tell the compiler that we should
Expand Down Expand Up @@ -302,13 +301,13 @@ pub struct RawAttachmentMut<'a> {
/// The following safety invariants are guaranteed to be upheld as long as
/// this struct exists:
///
/// 1. The pointer must have been created from a `Box<AttachmentData<A>>`
/// for some `A` using `Box::into_raw`.
/// 2. The pointer will point to the same `AttachmentData<A>` for the entire
/// 1. The pointer must have been created from a [`Box<AttachmentData<A>>`]
/// for some `A` using [`Box::into_raw`].
/// 2. The pointer will point to the same [`AttachmentData<A>`](AttachmentData) for the entire
/// lifetime of this object.
/// 3. This pointer represents exclusive mutable access to the
/// `AttachmentData` for the lifetime `'a` with the same semantics as a
/// `&'a mut AttachmentData<C>`.
/// [`AttachmentData`] for the lifetime `'a` with the same semantics as a
/// [`&'a mut AttachmentData<C>`](AttachmentData).
ptr: NonNull<AttachmentData<Erased>>,

/// Marker to tell the compiler that we should
Expand Down
8 changes: 4 additions & 4 deletions rootcause-internals/src/attachment/vtable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ pub(crate) struct AttachmentVtable {
/// Drops the [`Box<AttachmentData<A>>`] instance pointed to by this
/// pointer.
drop: unsafe fn(NonNull<AttachmentData<Erased>>),
/// Formats the attachment using the `display` method on the handler.
/// Formats the attachment using the [`AttachmentHandler::display`] method.
display: unsafe fn(RawAttachmentRef<'_>, &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
/// Formats the attachment using the `debug` method on the handler.
/// Formats the attachment using the [`AttachmentHandler::debug`] method on the handler.
debug: unsafe fn(RawAttachmentRef<'_>, &mut core::fmt::Formatter<'_>) -> core::fmt::Result,
/// Get the formatting style preferred by the attachment when formatted as
/// part of a report.
/// part of a report, according to [`AttachmentHandler::preferred_formatting_style`].
preferred_formatting_style:
unsafe fn(RawAttachmentRef<'_>, FormattingFunction) -> AttachmentFormattingStyle,
}
Expand Down Expand Up @@ -100,7 +100,7 @@ impl AttachmentVtable {
(self.handler_type_id)()
}

/// Drops the `Box<AttachmentData<A>>` instance pointed to by this pointer.
/// Drops the [`Box<AttachmentData<A>>`] instance pointed to by this pointer.
///
/// # Safety
///
Expand Down
49 changes: 30 additions & 19 deletions rootcause-internals/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,19 @@
/// # When to Implement
///
/// You typically don't need to implement this trait directly. The rootcause
/// library provides built-in handlers (`Error`, `Display`, `Debug`, `Any`) that
/// library provides built-in handlers ([`Error`], [`Display`], [`Debug`], [`Any`]) that
/// cover most use cases.
///
/// [`Error`]: https://docs.rs/rootcause/latest/rootcause/handlers/struct.Error.html
/// [`Debug`]: https://docs.rs/rootcause/latest/rootcause/handlers/struct.Debug.html
/// [`Display`]: https://docs.rs/rootcause/latest/rootcause/handlers/struct.Display.html
/// [`Any`]: https://docs.rs/rootcause/latest/rootcause/handlers/struct.Any.html
///
/// Implement this trait when you need custom formatting behavior that the
/// built-in handlers don't provide, such as:
/// - Custom source chain navigation for types that don't implement
/// `std::error::Error`
/// - Special display formatting that differs from the type's `Display`
/// [`std::error::Error`]
/// - Special display formatting that differs from the type's [`Display`](core::fmt::Display)
/// implementation
/// - Dynamic formatting based on the context value
///
Expand Down Expand Up @@ -85,14 +90,16 @@ pub trait ContextHandler<C>: 'static {
///
/// # Returns
///
/// - `Some(&dyn Error)` if this context has an underlying error source
/// - `None` if this context is a leaf in the error chain
/// - [`Some(&dyn Error)`](core::error::Error::source) if this context has an underlying error source
/// - [`None`] if this context is a leaf in the error chain
///
/// # Examples
///
/// For types implementing `std::error::Error`, delegate to their `source`
/// For types implementing [`std::error::Error`](core::error::Error), delegate to their [`source`]
/// method:
///
/// [`source`]: core::error::Error::source
///
/// ```
/// use std::error::Error;
///
Expand Down Expand Up @@ -176,9 +183,8 @@ pub trait ContextHandler<C>: 'static {
///
/// This method allows the handler to control:
/// - **Formatting**: Whether to use display or debug formatting
/// - **Source-chasing**: Whether to follow the chain of of
/// [`Error::source()`](core::error::Error::source) if
/// [`ContextHandler::source`] returns `Some`, and also how deep to chase.
/// - **Source-chasing**: Whether to follow the chain of of [`Error::source()`](core::error::Error::source) if [`ContextHandler::source`] returns [`Some`], and
/// also how deep to chase.
///
/// The default implementation returns the same formatting as the report,
/// with no source chasing.
Expand Down Expand Up @@ -414,11 +420,9 @@ pub trait AttachmentHandler<A>: 'static {
///
/// - `function`: Whether to use [`Display`](core::fmt::Display) or
/// [`Debug`](core::fmt::Debug) formatting
/// - `follow_source`: Whether to follow the
/// [`Error::source`](core::error::Error::source)-chain when
/// [`ContextHandler::source`] returns `Some`.
/// - `follow_source_depth`: How deep to follow the chain, `None` means no
/// limit.
/// - `follow_source`: Whether to follow the [`Error::source`](core::error::Error::source)-chain
/// when [`ContextHandler::source`] returns [`Some`].
/// - `follow_source_depth`: How deep to follow the chain, `None` means no limit.
///
/// # Default
///
Expand Down Expand Up @@ -449,7 +453,7 @@ pub struct ContextFormattingStyle {
/// formatting
pub follow_source: bool,
/// The maximum depth to follow the [`core::error::Error`] source chain when
/// formatting. Setting to `None` means unlimited depth.
/// formatting. Setting to [`None`] means unlimited depth.
pub follow_source_depth: Option<usize>,
}

Expand Down Expand Up @@ -514,8 +518,13 @@ pub struct AttachmentFormattingStyle {
///
/// # Variants
///
/// - **`Display`** (default): Use the `display` method
/// - **`Debug`**: Use the `debug` method
/// - **[`Display`]** (default): Use the [`ContextHandler::display`]
/// or [`AttachmentHandler::display`] method
/// - **[`Debug`]**: Use the [`ContextHandler::debug`]
/// or [`AttachmentHandler::debug`] method
///
/// [`Debug`]: FormattingFunction::Debug
/// [`Display`]: FormattingFunction::Display
///
/// # Examples
///
Expand All @@ -530,10 +539,12 @@ pub struct AttachmentFormattingStyle {
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub enum FormattingFunction {
/// Prefer display formatting via the `display` method.
/// Prefer display formatting via the
/// [`ContextHandler::display`]/[`AttachmentHandler::display`] method.
#[default]
Display,
/// Prefer debug formatting via the `debug` method.
/// Prefer debug formatting via the
/// [`ContextHandler::debug`]/[`AttachmentHandler::debug`] method.
Debug,
}

Expand Down
Loading
Loading