neuer_error/
lib.rs

1//! The error that can be whatever you want (it is Mr. Neuer). In every case (hopefully). NO AI
2//! SLOP!
3//!
4//! An error handling library designed to be:
5//!
6//! - Useful in both libraries and applications, containing human and machine information.
7//! - Ergonomic, low-boilerplate and comfortable, while still adhering best-practices and providing
8//!   all necessary infos.
9//! - Flexible in interfacing with other error handling libraries.
10//!
11//! ## Features/Highlights
12//!
13//! - Most importantly: error messages, that are helpful for debugging. By default it uses source
14//!   locations instead of backtraces, which is often easier to follow, more efficient and works
15//!   without debug info.
16//! - Discoverable, typed context getters without generic soup, type conversions and conflicts.
17//! - Works with std and no-std, but requires a global allocator.
18//! - Compatible with non-Send/Sync environments, but also with Send/Sync environments ([per feature
19//!   flag](#feature-flags)).
20//! - Out of the box source error chaining.
21//! - No dependencies by default. Optional features may lead to some dependencies.
22//! - No `unsafe` used (yet?).
23//!
24//! ## Why a new (German: neuer) error library?
25//!
26//! Long story, you can [view it here](https://github.com/FlixCoder/neuer-error/blob/main/why-another-lib.md).
27//!
28//! TLDR: I wasn't satisfied with my previous approach and existing libraries I know. And I was
29//! inspired by a blog post to experiment myself with error handling design.
30//!
31//! ## Usage
32//!
33//! The best way to see how to use it for your use-case is to check out the [examples](https://github.com/FlixCoder/neuer-error/tree/main/examples).
34//! Nevertheless, here is a quick demo:
35//!
36//! ```rust
37//! # use neuer_error::{traits::*, NeuErr, Result, provided_attachments};
38//! // In library/module:
39//! #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
40//! pub enum Retryable { No, Yes }
41//!
42//! // Provide discoverable, typed information for library users.
43//! provided_attachments!(
44//!   retryable(single: Retryable) -> bool {
45//!     |retryable| matches!(retryable, Some(Retryable::Yes))
46//!   };
47//! );
48//!
49//! fn do_something_internal() -> Result<()> {
50//!   Err(NeuErr::new("Error occurred internally")
51//!     .attach(Retryable::No))
52//! }
53//!
54//! pub fn do_something() -> Result<()> {
55//!   do_something_internal().context("Operation failed")
56//! }
57//!
58//! // In consumer/application:
59//! fn main() {
60//!   match do_something() {
61//!     Ok(()) => {}
62//!     Err(err) if err.retryable() => {
63//!       eprintln!("Retryable error");
64//!     }
65//!     Err(_) => {
66//!       eprintln!("Non-retryable error");
67//!     }
68//!   }
69//! }
70//! ```
71//!
72//! Run `cargo add neuer-error` to add the library to your project.
73//!
74//! ## Comparisons
75//!
76//! ### Anyhow / Eyre
77//!
78//! - `NeuErr` provides a meechanism to discover and retrieve multiple items of typed context
79//!   information, while `anyhow` can `downcast` to its source error types only.
80//! - `NeuErr` captures source locations instead of backtraces by default, which is more efficient
81//!   and works without debug info. I personally also find it easier to read.
82//!
83//! ### Thiserror / Snafu
84//!
85//! - `NeuErr` is a single error type for all errors, so no need for boilerplate, better ergonomics,
86//!   but less type safety and flexibility.
87//! - `NeuErr` captures source location automatically, which `thiserror` does not and `snafu` does
88//!   only when you add the location field to every error variant.
89//! - `NeuErr` prints the full (source) error chain already.
90//! - `NeuErr` does not have procedural macros.
91//!
92//! ## Feature Flags
93//!
94//! **default** -> std, send, sync: Default selected features. Deactivate with
95//! `default-features=false`.
96//!
97//! **std** (default): Enables use of `std`. Provides interaction with `ExitCode` termination.
98//!
99//! **send** (default): Requires all contained types to be `Send`, so that [`NeuErr`] is also
100//! `Send`.
101//!
102//! **sync** (default) -> send: Requires all contained types to be `Sync`, so that [`NeuErr`] is
103//! also `Sync`.
104//!
105//! **colors**: Activates colored error formatting via `yansi` (added dependency). When std it
106//! enabled, it also enables `yansi`'s automatic detection whether to use or not use colors. See
107//! `yansi`'s documentation on details.
108#![cfg_attr(not(feature = "std"), no_std)]
109#![warn(clippy::std_instead_of_core, clippy::std_instead_of_alloc, clippy::alloc_instead_of_core)]
110
111extern crate alloc;
112
113mod error;
114mod features;
115mod macros;
116mod results;
117
118pub use self::{
119	error::{NeuErr, NeuErrImpl},
120	results::{ConvertOption, ConvertResult, CtxResultExt, ResultExt},
121};
122
123pub mod traits {
124	//! All traits that need to be in scope for	comfortable usage.
125	pub use crate::{ConvertOption as _, ConvertResult as _, CtxResultExt as _, ResultExt as _};
126}
127
128/// `Result` type alias using the crate's [`NeuErr`] type.
129pub type Result<T, E = NeuErr> = ::core::result::Result<T, E>;
130
131/// Create a `Result::Ok` value with [`NeuErr`] as given error type.
132#[inline(always)]
133#[expect(non_snake_case, reason = "Mimics Result::Ok")]
134pub const fn Ok<T>(value: T) -> Result<T> {
135	Result::Ok(value)
136}
137
138#[cfg(test)]
139mod tests;