reportify
Errors are part of an API's contract, and deserve the same care as its types and
function signatures. This crate builds typed Rust errors that can still carry rich
diagnostic reports without turning every internal detail into that contract. The
error stays the curated, typed boundary programs branch on. Everything else, a
narrative, structured fields, a backtrace, a cause, and contributing factors, is
diagnostic detail that lives in context instead, regardless of who or what ends up
reading it.
use ;
new_whatever_type!
A Whatever type such as AppError above never carries its own message. Every report
needs an explicit description at the call site, instead of falling back to a generic,
type-level placeholder.
return_error! is a ?-like macro for a diverging function whose own return type is
already the bare error/Report, e.g., a server loop that only ever exits by failing.
ResultExt::assert_ok unwraps a result, treating an error as a bug in the program rather
than an external failure: it panics, showing the full rendered report, instead of
propagating.
Cause and Factors
Report::escalate turns a report into a differently-typed one, keeping the original as its
cause. This is the only way a cause gets set: there is no way to attach a cause to an
already-existing report, only to derive a new report from an old one.
Report::with_factor/Report::with_factors attach one or more independent, contributing
factors instead, e.g., several hooks that each failed. Unlike a cause, a factor makes no
claim that it alone was necessary or sufficient.
Capturing Panics
A panic caught with catch_unwind becomes a Report<Panicked> with a correct backtrace
and location. A bare std::panic::catch_unwind cannot recover either on its own, since
by the time it returns, the stack has already unwound. Panicked keeps the raw panic
payload, not just an extracted message, so callers can still distinguish a genuine bug
from a deliberate, non-error use of resume_unwind, e.g., as a cancellation signal.
Call install_panic_hook near the top of main to install catch_unwind's hook
eagerly, closing a narrow race where a panic on another thread at that exact moment could
otherwise slip through uncaptured. install_pretty_panic_hook/
install_pretty_panic_hook_with additionally take over how an uncaught panic prints:
instead of the default hook's plain banner, it renders the same way a Report does,
always flagged as a bug, optionally with an issue tracker URL and extra fields like the
application's own version through PrettyPanicOptions.
Logging
ResultExt::log_error/log_warning/log_info log a report through tracing at the
matching level; ResultExt::ignore is log_error with the value discarded too. The
rendered report becomes the event's message; the error's type name and code (when it has
one) are attached as separate error.type/error.code fields, so a structured
subscriber can filter or group on them without parsing the message text.
Rendering
Display/Debug render a report as a tree, causes and factors nested under arrows.
Report::render takes explicit render::RenderOptions for plain ASCII instead of
Unicode box-drawing, forced or disabled color, or a compact view without locations.
Report::print/Report::eprint render and print directly to stdout/stderr, correcting
the color mode to check whichever stream they actually print to. A verbose backtrace
skips reportify's own frames and the runtime's startup frames, keeping only what the
caller actually wrote. RenderOptions::wrap word-wraps long lines to a fixed width or
the actual terminal width, off by default, so a long message/suggestion/field value
still lines up under the tree instead of soft-wrapping wherever the terminal decides. See
the render module's docs for what each combination looks like, or run cargo run --example config.
Export
Report::export/Report::export_with turn a report into structured data instead of
text. Public fields are exported by default. Sensitive and secret fields, and the
captured backtrace and span trace, require explicit opt-in through
export::ExportOptions. The backtrace and span trace export as structured frames/spans,
not rendered text, and the backtrace is the raw, unfiltered capture.
Optional features:
backtracecaptures backtraces through thebacktracecrate instead ofstd::backtrace, enabling frame-skipped verbose rendering, and is enabled by default.colorcolors rendered reports throughconsoleand is enabled by default.spantracecapturestracing-errorspan traces and is enabled by default.serdederivesSerialize/Deserializefor exported report data, withcamelCasefield names (enum variant tags stay Capitalized).