Skip to main content

oopsie

Attribute Macro oopsie 

Source
#[oopsie]
Expand description

Attribute macro — the primary way to define an oopsie error type.

Generates context selectors, Display, Error, and Debug impls in one attribute. No need for #[derive(Debug, Oopsie)].

§Usage

use oopsie::ResultExt as _;

#[oopsie::oopsie]
pub enum MyError {
    #[oopsie("Connection to {host} failed")]
    Connect { host: String, source: std::io::Error },
}

let result: Result<(), std::io::Error> = Err(std::io::Error::other("refused"));
let err = result.context(my_oopsies::Connect { host: "db.example.com" }).unwrap_err();
assert_eq!(err.to_string(), "Connection to db.example.com failed");

§Diagnostics

Pass traced to automatically inject a backtrace field (and a span-trace when the tracing feature is enabled):

#[oopsie::oopsie(traced)]
pub enum MyError {
    #[oopsie("Connection failed")]
    Connect,
}

§Parameters

The bare #[oopsie] adds no diagnostics; it is equivalent to #[derive(Debug, Oopsie)]. The arguments below tune what gets generated; each has its own subsection further down.

ParameterEffect
tracedInject a backtrace field (and a span-trace under the tracing feature), plus an auto error code
pathPath to the oopsie crate in generated impls
debugSkip the automatic Debug derive

Injects trace-capture fields into every variant (or the struct): a backtrace, a span trace, and the caller location, all enabled by default. The nested options tune each part — mentioning one never disables the others — and traced(false) opts the type out entirely. With traced active, an auto-generated error code is also attached to each variant unless the nested code option disables it.

On enums, a variant can override tracing with #[oopsie(traced)] or #[oopsie(traced = false)]. This toggle is boolean-only at variant scope: it enables or disables the variant’s traced injection as a whole. It only applies when the enum itself is traced; a variant traced on an enum that isn’t traced is a compile error.

Forms: traced, traced = true, traced = false, traced(true), traced(false), traced(enabled = ...), traced(backtrace(...), spantrace(...), timestamp(...), location = ..., packed = ..., boxed = ..., code(...)).

Variant forms (enum variants only): traced, traced = true, traced = false.

§Example

#[oopsie::oopsie(traced)]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Overrides the path to the oopsie crate used in the generated code, given as a string. Needed when oopsie is re-exported under a different path or renamed in Cargo.toml. Defaults to ::oopsie; a container-level #[oopsie(path = ...)] on the type itself wins over this.

Forms: path = "some::path", path = some::path.

§Example

// A real custom path must name a crate that actually re-exports `oopsie`,
// which can't exist inside this crate's own doctest.
#[oopsie::oopsie(path = "my_crate::reexports::oopsie")]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

By default #[oopsie::oopsie] adds a Debug derive to the error type (a Debug impl is required by std::error::Error). Set debug = false to skip that injection when you supply a hand-written impl Debug instead. An existing Debug derive on the type is always respected and never duplicated.

Forms: debug, debug = true, debug = false, debug(true), debug(false).

§Example

use std::fmt;

#[oopsie::oopsie(debug = false)]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

impl fmt::Debug for AppError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("custom debug")
    }
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(format!("{err:?}"), "custom debug");
}

§traced(...) options

These keys are spelled nested inside traced(...). backtrace/spantrace take an optional settings block (r#type, boxed, enabled); timestamp takes chrono/provide; packed/boxed are pair-level layout flags.

OptionEffect
backtraceEnable/tune the captured backtrace (default on)
spantraceEnable/tune the captured span trace (default on; captures nothing without the tracing feature)
timestampInject an auto-captured timestamp field (default off)
locationInject an auto-captured call-site Location field (default on)
packedStore both traces as one (Backtrace, SpanTrace) field
boxedBox the injected trace field(s)
codeTune (or disable) the auto error code
chronoUse chrono::DateTime<Local> timestamps
provideExpose the timestamp via the provider API
r#typeOverride the injected type for this part
enabledExplicit on/off inside a settings block

Inside traced(...): enables and tunes capture of the backtrace (default: on). Mentioning this part never disables the others. backtrace(false) drops the backtrace, and a settings block tunes its type, boxing, and on/off state.

Forms: backtrace, backtrace = true, backtrace = false, backtrace(true), backtrace(false), backtrace(r#type = Path, boxed = ..., enabled = ...).

§Example

#[oopsie::oopsie(traced(backtrace, spantrace(false)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Inside traced(...): enables and tunes capture of the span trace (default: on). Mentioning this part never disables the others. spantrace(false) drops the span trace, and a settings block tunes its type, boxing, and on/off state. Without the tracing feature the span trace captures nothing.

Forms: spantrace, spantrace = true, spantrace = false, spantrace(true), spantrace(false), spantrace(r#type = Path, boxed = ..., enabled = ...).

§Example

#[oopsie::oopsie(traced(spantrace, backtrace(false)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Inside traced(...): injects an auto-captured timestamp field (default: off). A bare timestamp records a SystemTime; a settings block opts into the chrono type or the provider exposure.

Forms: timestamp, timestamp = true, timestamp = false, timestamp(true), timestamp(false), timestamp(chrono = ..., provide = ..., enabled = ...).

§Example

#[oopsie::oopsie(traced(timestamp))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Inside traced(...): injects an auto-captured &'static Location<'static> field recording the call site where the error was built (default: on). The location is surfaced via Diagnostic::oopsie_location and rendered by Report even when backtraces are disabled. Use location = false to opt out.

Forms: location, location = true, location = false, location(true), location(false).

§Example

#[oopsie::oopsie(traced(location = false))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Inside traced(...): stores the backtrace and span trace as one packed (Backtrace, SpanTrace) field (default: on). packed = false keeps them as two separate fields, which also allows boxing each independently. Packing requires backtrace and spantrace to share one boxing mode.

Forms: packed, packed = true, packed = false, packed(true), packed(false).

§Example

#[oopsie::oopsie(traced(packed = false))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Inside traced(...): boxes the injected trace field(s) (default: on), keeping the error type small. At the pair level it applies to both traces; placed inside backtrace(...) or spantrace(...) it tunes that one trace. boxed = false stores the trace(s) inline.

Forms: boxed, boxed = true, boxed = false, boxed(true), boxed(false).

§Example

#[oopsie::oopsie(traced(boxed = false))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

With traced, attaches an auto-generated error code to every variant (or the struct) that has no explicit #[oopsie(code = "...")], surfaced via Diagnostic::oopsie_error_code. The generated code is the item’s path, module_path::Type::Variant; transparent items are excluded. traced(code = false) turns the auto code off, and traced(code(r#type = Path)) overrides the error-code type (default ErrorCode).

Forms: traced(code), traced(code = true), traced(code = false), traced(code(true)), traced(code(false)), traced(code(r#type = Path, enabled = ...)).

§Example

use oopsie::Diagnostic as _;

#[oopsie::oopsie(traced(code(r#type = ::oopsie::ErrorCode)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert!(err.oopsie_error_code().is_some());
}

Inside timestamp(...): uses chrono::DateTime<Local> instead of SystemTime as the injected timestamp type (default: off). Opt-in, and needs oopsie’s chrono feature enabled.

Forms: chrono, chrono = true, chrono = false, chrono(true), chrono(false).

§Example

// `chrono = true` only compiles with oopsie's `chrono` feature, which this
// crate's own doctests don't enable.
#[oopsie::oopsie(traced(timestamp(chrono = true)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

Inside timestamp(...): also exposes the captured timestamp through the std::error::Request provider API (active with the unstable feature), so callers can retrieve it with Error::request_value (default: off). Opt-in.

Forms: provide, provide = true, provide = false, provide(true), provide(false).

§Example

#[oopsie::oopsie(traced(timestamp(provide = true)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

Overrides the injected type for this part: the trace field type inside backtrace(...) or spantrace(...), or the error-code type inside code(...). Spelled with the raw identifier r#type since type is a keyword.

Forms: r#type = "some::Path", r#type = some::Path.

§Example

#[oopsie::oopsie(traced(backtrace(r#type = ::oopsie::Backtrace)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

An explicit on/off switch accepted in any settings block, e.g. backtrace(enabled = false, r#type = ...). It lets a block both carry other settings and state whether the part is active; a settings block without it counts as enabled.

Forms: enabled, enabled = true, enabled = false.

§Example

#[oopsie::oopsie(traced(backtrace(enabled = false)))]
pub enum AppError {
    #[oopsie("boom")]
    Boom,
}

fn main() {
    let err = app_oopsies::Boom.build();
    assert_eq!(err.to_string(), "boom");
}

With traced, every variant (or the struct itself) also gets an automatic error code unless it is transparent or carries an explicit #[oopsie(code = "...")]. The code is module_path::Type for structs and module_path::Type::Variant for enum variants; Report renders it as Error[...]: in the report header.

Container-level #[oopsie(...)] attributes (module, vis, size, etc.) are placed on the type itself, not in the attribute macro’s argument list.