gix_error/lib.rs
1//! Common error types and utilities for error handling.
2//!
3//! # Usage
4//!
5//! * When there is **no callee error** to track, use *simple* `std::error::Error` implementations directly,
6//! e.g. `Result<_, Simple>`.
7//! * When there **is callee error to track** *in a `gix-plumbing`*, use e.g. `Result<_, Exn<Simple>>`.
8//! - Remember that `Exn<T>` does not implement `std::error::Error` so it's not easy to use outside `gix-` crates.
9//! - Use the type-erased version in callbacks like [`Exn`] (without type arguments), i.e. `Result<T, Exn>`.
10//! * When there **is callee error to track** *in the `gix` crate*, convert both `std::error::Error` and `Exn<E>` into [`Error`]
11//!
12//! # Standard Error Types
13//!
14//! These should always be used if they match the meaning of the error well enough instead of creating an own
15//! [`Error`](std::error::Error)-implementing type, and used with
16//! [`ResultExt::or_raise(<StandardErrorType>)`](ResultExt::or_raise) or
17//! [`OptionExt::ok_or_raise(<StandardErrorType>)`](OptionExt::ok_or_raise), or sibling methods.
18//!
19//! All these types implement [`Error`](std::error::Error).
20//!
21//! ## [`Message`]
22//!
23//! The baseline that provides a formatted message.
24//! Formatting can more easily be done with the [`message!`] macro as convenience, roughly equivalent to
25//! [`Message::new(format!("…"))`](Message::new) or `format!("…").into()`.
26//!
27//! ## Specialised types
28//!
29//! - [`ParseError`]
30//! - like [`Message`], but can optionally store the input that caused the failure.
31//!
32//! # [`Exn<ErrorType>`](Exn) and [`Exn`]
33//!
34//! The [`Exn`] type does not implement [`Error`](std::error::Error) itself, but is able to store causing errors
35//! via [`ResultExt::or_raise()`] (and sibling methods) as well as location information of the creation site.
36//!
37//! While plumbing functions that need to track causes should always return a distinct type like [`Exn<Message>`](Exn),
38//! if that's not possible, use [`Exn::erased`] to let it return `Result<T, Exn>` instead, allowing any return type.
39//!
40//! A side effect of this is that any callee that causes errors needs to be annotated with
41//! `.or_raise(|| message!("context information"))` or `.or_raise_erased(|| message!("context information"))`.
42//!
43//! # Feature Flags
44#![cfg_attr(
45 all(doc, feature = "document-features"),
46 doc = ::document_features::document_features!()
47)]
48//! # Why not `anyhow`?
49//!
50//! `anyhow` is a proven and optimized library, and it would certainly suffice for an error-chain based approach
51//! where users are expected to downcast to concrete types.
52//!
53//! What's missing though is `track-caller` which will always capture the location of error instantiation, along with
54//! compatibility for error trees, which are happening when multiple calls are in flight during concurrency.
55//!
56//! Both libraries share the shortcoming of not being able to implement `std::error::Error` on their error type,
57//! and both provide workarounds.
58//!
59//! `exn` is much less optimized, but also costs only a `Box` on the stack,
60//! which in any case is a step up from `thiserror` which exposed a lot of heft to the stack.
61#![deny(missing_docs, unsafe_code)]
62/// A result type to hide the [Exn] error wrapper.
63mod exn;
64
65pub use bstr;
66pub use exn::{ErrorExt, Exn, Frame, OptionExt, ResultExt, Something, Untyped};
67
68/// An error type that wraps an inner type-erased boxed `std::error::Error` or an `Exn` frame.
69///
70/// In that, it's similar to `anyhow`, but with support for tracking the call site and trees of errors.
71///
72/// # Warning: `source()` information is stringified and type-erased
73///
74/// All `source()` values when created with [`Error::from_error()`] are turned into frames,
75/// but lose their type information completely.
76/// This is because they are only seen as reference and thus can't be stored.
77///
78/// # The `auto-chain-error` feature
79///
80/// If it's enabled, this type is merely a wrapper around [`ChainedError`]. This happens automatically
81/// so applications that require this don't have to go through an extra conversion.
82///
83/// When both the `tree-error` and `auto-chain-error` features are enabled, the `tree-error`
84/// behavior takes precedence and this type uses the tree-based representation.
85pub struct Error {
86 #[cfg(any(feature = "tree-error", not(feature = "auto-chain-error")))]
87 inner: error::Inner,
88 #[cfg(all(feature = "auto-chain-error", not(feature = "tree-error")))]
89 inner: ChainedError,
90}
91
92mod error;
93
94/// Various kinds of concrete errors that implement [`std::error::Error`].
95mod concrete;
96pub use concrete::chain::ChainedError;
97pub use concrete::message::{message, Message};
98pub use concrete::parse::ParseError;
99
100pub(crate) fn write_location(f: &mut std::fmt::Formatter<'_>, location: &std::panic::Location) -> std::fmt::Result {
101 write!(f, ", at {}:{}", location.file(), location.line())
102}