nmbrs_errorhandler/lib.rs
1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! # nmbrs-errorhandler
5//!
6//! Contract & axioms: [SRD 07](../../docs/SRD/07_error_routing.md).
7//!
8//! Modular composable error handler. Errors are classified by
9//! type name (regex) and routed through a chain of handlers that
10//! can log, count, meter, retry, or stop execution.
11//!
12//! Designed for nmbrs's op-dispatch loop, where every adapter op
13//! result might be `Ok` or one of dozens of named error variants
14//! — and operators want different policies for different error
15//! families (retry timeouts, count + ignore `WriteFailures`,
16//! stop on `BadCredentials`).
17//!
18//! ## Pieces
19//!
20//! - [`ErrorDetail`] is the structured error a producer hands
21//! in. Carries a name (used for routing), a retryable flag,
22//! and an optional result code.
23//! - [`ErrorHandler`] is the trait every leaf handler implements
24//! (`StopHandler`, `WarnHandler`, `RetryHandler`,
25//! `CounterHandler`, …). Each one decides how to react to the
26//! incoming detail and may flip `retryable` or `stop` flags on it.
27//! - [`ErrorRouter`] holds a list of `(regex, handler chain)`
28//! entries and dispatches each incoming detail to the first
29//! matching chain.
30//!
31//! ## Config syntax
32//!
33//! Routes are declared as semicolon-separated `pattern:chain`
34//! pairs. Inside a chain, `,` separates handler names:
35//!
36//! ```text
37//! "TimeoutError:retry,warn,counter;.*:stop"
38//! ```
39//!
40//! Reads as: a `TimeoutError` retries (and warns + counts on
41//! each attempt); anything else stops the run.
42//!
43//! ```
44//! use nmbrs_errorhandler::ErrorRouter;
45//!
46//! let router = ErrorRouter::parse(
47//! "TimeoutError:retry,warn,counter;.*:stop",
48//! ).expect("config parses");
49//! # drop(router); // exercised by integration tests in nmbrs/tests/
50//! ```
51//!
52//! ## Building details
53//!
54//! [`ErrorDetail`] uses a builder-style API:
55//!
56//! ```
57//! use nmbrs_errorhandler::ErrorDetail;
58//!
59//! let d = ErrorDetail::retryable("TimeoutError")
60//! .with_result_code(503);
61//! assert!(d.is_retryable());
62//! assert_eq!(d.name, "TimeoutError");
63//! ```
64//!
65//! ## Defaults
66//!
67//! For tests and one-line setups:
68//!
69//! - [`ErrorRouter::default_stop`] — stop on any error.
70//! - [`ErrorRouter::default_warn_count`] — warn + count on any
71//! error, never stop. Convenient for diagnostic runs.
72
73mod detail;
74mod handler;
75pub mod handlers;
76mod router;
77
78pub use detail::{ErrorDetail, Retry};
79pub use handler::ErrorHandler;
80pub use router::ErrorRouter;