Skip to content

Commit 10f0d78

Browse files
author
Flix
committed
Rename CtxError to NeuErr and implement colored format
1 parent 3a06817 commit 10f0d78

16 files changed

Lines changed: 339 additions & 142 deletions

Cargo.lock

Lines changed: 106 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@ exclude = ["/.github"]
1717
default = ["std", "send", "sync"]
1818
# Use the standard library.
1919
# Provides interaction with `ExitCode` termination.
20-
std = []
20+
std = ["yansi?/std", "yansi?/detect-tty", "yansi?/detect-env"]
2121
# Error types must be `Send`.
2222
send = []
2323
# Error types must be `Sync`.
2424
sync = ["send"]
25+
# Enable colored error formatting. See `yansi` create documentation on how to control enable/disable colors.
26+
colors = ["dep:yansi"]
2527

2628
[dependencies]
29+
yansi = { version = "1.0.1", optional = true, default-features = false, features = ["alloc"] }
2730

2831
[dev-dependencies]
2932
regex = { version = "1.12.2", default-features = false, features = ["unicode", "perf"] }

Makefile.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,18 @@ command = "cargo"
7171
args = ["clippy", "--workspace", "--all-targets", "--no-default-features", "--features", "std,send,sync", "--", "-D", "warnings"]
7272
dependencies = ["install-rust-toolchain"]
7373

74+
[tasks.clippy-colors]
75+
install_crate = false
76+
command = "cargo"
77+
args = ["clippy", "--workspace", "--all-targets", "--features", "colors", "--", "-D", "warnings"]
78+
dependencies = ["install-rust-toolchain"]
79+
80+
[tasks.clippy-colors-no-std]
81+
install_crate = false
82+
command = "cargo"
83+
args = ["clippy", "--workspace", "--all-targets", "--no-default-features", "--features", "colors", "--", "-D", "warnings"]
84+
dependencies = ["install-rust-toolchain"]
85+
7486
[tasks.clippy-all]
7587
install_crate = false
7688
command = "cargo"
@@ -86,6 +98,8 @@ dependencies = [
8698
"clippy-no-send-sync",
8799
"clippy-send",
88100
"clippy-send-sync",
101+
"clippy-colors",
102+
"clippy-colors-no-std",
89103
"clippy-all",
90104
]
91105

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ An error handling library designed to be:
1919
- Works with std and no-std, but requires a global allocator. [See example](examples/embedded-no-std.rs).
2020
- Compatible with non-Send/Sync environments, but also with Send/Sync environments (per feature flag).
2121
- Out of the box source error chaining.
22+
- No dependencies by default. Optional features may lead to some dependencies.
23+
- No `unsafe` used (yet?).
2224

2325
## Why a new (German: neuer) error library?
2426

@@ -44,7 +46,7 @@ provided_attachments!(
4446
);
4547

4648
fn do_something_internal() -> Result<()> {
47-
Err(CtxError::new("Error occurred internally")
49+
Err(NeuErr::new("Error occurred internally")
4850
.attach(Retryable::No))
4951
}
5052

@@ -68,6 +70,20 @@ fn main() {
6870

6971
Run `cargo add neuer-error` to add the library to your project.
7072

73+
## Comparisons
74+
75+
### Anyhow / Eyre
76+
77+
- `NeuErr` provides a meechanism to discover and retrieve multiple items of typed context information, while `anyhow` can `downcast` to its source error types only.
78+
- `NeuErr` captures source locations instead of backtraces by default, which is more efficient and works without debug info. I personally also find it easier to read.
79+
80+
### Thiserror / Snafu
81+
82+
- `NeuErr` is a single error type for all errors, so no need for boilerplate, better ergonomics, but less type safety and flexibility.
83+
- `NeuErr` captures source location automatically, which `thiserror` does not and `snafu` does only when you add the location field to every error variant.
84+
- `NeuErr` prints the full (source) error chain already.
85+
- `NeuErr` does not have procedural macros.
86+
7187
## Development
7288

7389
If you want to contribute or have questions, feel free to open issues :)

examples/embedded-no-std.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ use ::core::{
1414
ptr::null_mut,
1515
sync::atomic::{AtomicUsize, Ordering},
1616
};
17-
use ::neuer_error::{CtxError, Result, traits::*};
17+
use ::neuer_error::{NeuErr, Result, traits::*};
1818

1919

2020
fn self_test() -> Result<()> {
21-
Err(CtxError::new("Memory error"))
21+
Err(NeuErr::new("Memory error"))
2222
}
2323

2424
fn boot_up() -> Result<()> {

examples/library.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@
88

99
use ::neuer_error::Result;
1010

11-
use self::library::CtxErrorAttachments;
11+
use self::library::NeuErrAttachments;
1212

1313
/// Library creators need to think about what information would be interesting for humans and what
1414
/// machine information is necessary to handle errors programmatically.
1515
///
1616
/// When providing attachments, library authors should make use of the `provided_attachments!`
1717
/// macro!
1818
mod library {
19-
use ::neuer_error::{CtxError, Result, provided_attachments, traits::*};
19+
use ::neuer_error::{NeuErr, Result, provided_attachments, traits::*};
2020

2121
/// Kinds of errors that are interesting to match on for library users.
2222
/// If it is only interesting to humans, it can be iin the context instead.
@@ -44,7 +44,7 @@ mod library {
4444

4545
/// Implement your errors while attaching machine-targeted information.
4646
fn do_something_internal() -> Result<()> {
47-
Err(CtxError::new("Error occurred internally")
47+
Err(NeuErr::new("Error occurred internally")
4848
.attach(ErrorKind::InvalidInput)
4949
.attach(Retryable::No))
5050
}

examples/non-dev-user.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
reason = "Example"
77
)]
88

9-
use ::neuer_error::{CtxError, Result, provided_attachments, traits::*};
9+
use ::neuer_error::{NeuErr, Result, provided_attachments, traits::*};
1010

1111
/// The usual error message contains code file locations, so it is not always suitable for UI
1212
/// errors. We can simply attach user friendly messages to the errors and still keep debuggable
@@ -23,7 +23,7 @@ provided_attachments!(
2323
struct MyUser;
2424

2525
fn fetch_user() -> Result<MyUser> {
26-
Err(CtxError::new("Failed!").attach(UserErrorMessage("Invalid user ID 5".to_owned())))
26+
Err(NeuErr::new("Failed!").attach(UserErrorMessage("Invalid user ID 5".to_owned())))
2727
}
2828

2929
fn save_user(_user: MyUser) -> Result<()> {

examples/tool-cli.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@
66
reason = "Example"
77
)]
88

9-
use ::neuer_error::{CtxError, Result, traits::*};
9+
use ::neuer_error::{NeuErr, Result, traits::*};
1010
use ::std::process::ExitCode;
1111

1212
fn ensure_project_validity() -> Result<()> {
1313
Ok(())
1414
}
1515

1616
fn call_preprocessor() -> Result<()> {
17-
Err(CtxError::new("Binary gcc not found"))
17+
Err(NeuErr::new("Binary gcc not found"))
1818
}
1919

2020
fn compile_my_code() -> Result<()> {
21-
ensure_project_validity().context("project must be valid for compiling")?;
22-
call_preprocessor().context("preprocessor failed")?;
21+
ensure_project_validity().context("Project must be valid for compiling")?;
22+
call_preprocessor().context("Üreprocessor failed")?;
2323
Ok(())
2424
}
2525

2626
fn lint() -> Result<()> {
27-
Err(CtxError::new("Warning: something is deprecated").attach_override(ExitCode::SUCCESS))
27+
Err(NeuErr::new("Warning: something is deprecated").attach_override(ExitCode::SUCCESS))
2828
}
2929

3030
// Returning the error will automatically use the attached ExitCode or assume failure.

examples/validation.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
reason = "Example"
77
)]
88

9-
use ::neuer_error::{CtxError, Result, traits::*};
9+
use ::neuer_error::{NeuErr, Result, traits::*};
1010

1111
struct UserData {
1212
id: u64,
@@ -15,14 +15,14 @@ struct UserData {
1515
}
1616

1717
fn validate_id(id: u64) -> Result<()> {
18-
if id == 0 { Err(CtxError::new("ID must be non-zero")) } else { Ok(()) }
18+
if id == 0 { Err(NeuErr::new("ID must be non-zero")) } else { Ok(()) }
1919
}
2020

2121
fn validate_name(name: &str) -> Result<()> {
2222
if name.trim().is_empty() {
23-
Err(CtxError::new("Name must not be empty"))
23+
Err(NeuErr::new("Name must not be empty"))
2424
} else if !name.chars().all(|c| c.is_alphabetic()) {
25-
Err(CtxError::new("Name must only contain alphabetic characters"))
25+
Err(NeuErr::new("Name must only contain alphabetic characters"))
2626
} else {
2727
Ok(())
2828
}
@@ -35,19 +35,19 @@ struct User {
3535
}
3636

3737
impl User {
38-
fn new(data: UserData) -> Result<Self, Vec<CtxError>> {
38+
fn new(data: UserData) -> Result<Self, Vec<NeuErr>> {
3939
let mut errors = Vec::new();
4040
let UserData { id, name, balance } = data;
4141

4242
validate_id(id).or_collect(&mut errors);
4343
validate_name(&name).or_collect(&mut errors);
4444

4545
if balance < 0 {
46-
errors.push(CtxError::new("Cannot create new user with debt"));
46+
errors.push(NeuErr::new("Cannot create new user with debt"));
4747
}
4848

4949
if id == 3 {
50-
errors.push(CtxError::new(format!("User {id} ({name}) already exists")));
50+
errors.push(NeuErr::new(format!("User {id} ({name}) already exists")));
5151
}
5252

5353
let user = User { id, name, balance };

examples/web-client.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
reason = "Example"
77
)]
88

9-
use ::neuer_error::{CtxError, Result, provided_attachments, traits::*};
9+
use ::neuer_error::{NeuErr, Result, provided_attachments, traits::*};
1010
use ::std::time::Duration;
1111

1212
/// Mark errors whether they can be retried and/or were already retried.
@@ -41,15 +41,15 @@ fn fetch_data(user: &str) -> Result<()> {
4141
std::io::ErrorKind::NetworkDown => ErrorStatus::Temporary,
4242
_ => ErrorStatus::Permanent,
4343
})
44-
.context_with(|| format!("failed fetching data for user {user}"))?;
44+
.context_with(|| format!("Failed fetching data for user {user}"))?;
4545

4646
// Alternative 2.
4747
do_request(request).map_err(|err| {
4848
let status = match err.kind() {
4949
std::io::ErrorKind::NetworkDown => ErrorStatus::Temporary,
5050
_ => ErrorStatus::Permanent,
5151
};
52-
CtxError::new_with_source(format!("failed fetching data for user {user}"), err)
52+
NeuErr::new_with_source(format!("Failed fetching data for user {user}"), err)
5353
.attach_override(status)
5454
})
5555
}

0 commit comments

Comments
 (0)