-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Approved by: @gshuflin ============ This PR moves the `error-stack` codebase into a module of `bigerror`. This opens up a lot of feature work such as: * blanket implementations on `Report<C>` types * new methods onto `Report<C>` and `Frame` without trait imports * simplify naming schemes; ex: `attach_printable_lazy` -> `attach_lazy` NOTE: this is a major breaking change
- Loading branch information
Showing
116 changed files
with
10,824 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,76 @@ | ||
[package] | ||
name = "bigerror" | ||
version = "0.8.1" | ||
version = "0.9.0" | ||
edition = "2021" | ||
description = "handle big errors ¯\\_(ツ)_/¯" | ||
license = "MIT" | ||
|
||
[features] | ||
default = ["std"] | ||
std = ["error-stack/std", "error-stack/anyhow"] | ||
spantrace = ["error-stack/spantrace"] | ||
eyre = ["error-stack/eyre"] | ||
serde = ["error-stack/serde"] | ||
hooks = ["error-stack/hooks"] | ||
grpc=["tonic", "tonic-types"] | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
spantrace = ["dep:tracing-error", "std"] | ||
std = ["anyhow?/std"] | ||
eyre = ["dep:eyre", "std"] | ||
serde = ["dep:serde"] | ||
hooks = ['dep:spin'] | ||
|
||
grpc=["tonic", "tonic-types"] | ||
|
||
[dependencies] | ||
error-stack = "0.4.1" | ||
thiserror = "1" | ||
tracing = "0.1" | ||
tonic = { version = "0.11", optional = true } | ||
tonic-types = { version = "0.11", optional = true } | ||
|
||
tracing-error = { version = "0.2", optional = true, default-features = false } | ||
anyhow = { version = ">=1.0.73", default-features = false, optional = true } | ||
eyre = { version = "0.6", default-features = false, optional = true } | ||
serde = { version = "1", default-features = false, optional = true } | ||
spin = { version = "0.9", default-features = false, optional = true, features = ['rwlock', 'once'] } | ||
|
||
[dev-dependencies] | ||
serde = { version = "1.0.200", features = ["derive"] } | ||
serde_json = "1.0.116" | ||
futures = { version = "0.3.30", default-features = false, features = ["executor"] } | ||
trybuild = "1.0.93" | ||
tracing = "0.1.40" | ||
tracing-subscriber = "0.3.18" | ||
insta = { version = "1.38.0", features = ['filters', 'ron'] } | ||
regex = "1.10.4" | ||
expect-test = "1.5.0" | ||
ansi-to-html = "0.2.1" | ||
once_cell = "1.19.0" | ||
supports-color = "3.0.0" | ||
supports-unicode = "3.0.0" | ||
owo-colors = "4.0.0" | ||
thiserror = "1.0.59" | ||
|
||
[package.metadata.docs.rs] | ||
all-features = true | ||
cargo-args = ["-Z", "unstable-options", "-Z", "rustdoc-scrape-examples"] | ||
targets = ["x86_64-unknown-linux-gnu"] | ||
|
||
[[example]] | ||
name = "demo" | ||
required-features = ["std"] | ||
doc-scrape-examples = true | ||
|
||
[[example]] | ||
name = "exit_code" | ||
required-features = ["std"] | ||
doc-scrape-examples = true | ||
|
||
[[example]] | ||
name = "parse_config" | ||
required-features = ["std"] | ||
doc-scrape-examples = true | ||
|
||
[[example]] | ||
name = "detect" | ||
required-features = ['std'] | ||
doc-scrape-examples = true | ||
|
||
|
||
[[test]] | ||
name = "common" | ||
test = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// This is the same example also used in the README.md. When updating this, don't forget updating | ||
// the README.md as well. This is mainly used to test the code and generate the output shown. | ||
|
||
use core::fmt; | ||
|
||
use bigerror::{error_stack::Result, Context, Report, ResultExt}; | ||
|
||
#[derive(Debug)] | ||
struct ParseExperimentError; | ||
|
||
impl fmt::Display for ParseExperimentError { | ||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt.write_str("invalid experiment description") | ||
} | ||
} | ||
|
||
impl Context for ParseExperimentError {} | ||
|
||
// Reason: false-positive, try_fold is fail-fast, our implementation is fail-slow. | ||
#[allow(clippy::manual_try_fold)] | ||
fn parse_experiment(description: &str) -> Result<Vec<(u64, u64)>, ParseExperimentError> { | ||
let values = description | ||
.split(' ') | ||
.map(|value| { | ||
value | ||
.parse::<u64>() | ||
.attach_printable_lazy(|| format!("{value:?} could not be parsed as experiment")) | ||
}) | ||
.map(|value| value.map(|ok| (ok, 2 * ok))) | ||
.fold(Ok(vec![]), |accum, value| match (accum, value) { | ||
(Ok(mut accum), Ok(value)) => { | ||
accum.push(value); | ||
|
||
Ok(accum) | ||
} | ||
(Ok(_), Err(err)) => Err(err), | ||
(Err(accum), Ok(_)) => Err(accum), | ||
(Err(mut accum), Err(err)) => { | ||
accum.extend_one(err); | ||
|
||
Err(accum) | ||
} | ||
}) | ||
.change_context(ParseExperimentError)?; | ||
|
||
Ok(values) | ||
} | ||
|
||
#[derive(Debug)] | ||
struct ExperimentError; | ||
|
||
impl fmt::Display for ExperimentError { | ||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt.write_str("experiment error: could not run experiment") | ||
} | ||
} | ||
|
||
impl Context for ExperimentError {} | ||
|
||
#[allow(clippy::manual_try_fold)] | ||
fn start_experiments( | ||
experiment_ids: &[usize], | ||
experiment_descriptions: &[&str], | ||
) -> Result<Vec<u64>, ExperimentError> { | ||
let experiments = experiment_ids | ||
.iter() | ||
.map(|exp_id| { | ||
let description = experiment_descriptions.get(*exp_id).ok_or_else(|| { | ||
Report::new(ExperimentError) | ||
.attach_printable(format!("experiment {exp_id} has no valid description")) | ||
})?; | ||
|
||
let experiments = parse_experiment(description) | ||
.attach_printable(format!("experiment {exp_id} could not be parsed")) | ||
.change_context(ExperimentError)?; | ||
|
||
let experiments = experiments | ||
.into_iter() | ||
.map(|(a, b)| move || a * b) | ||
.collect::<Vec<_>>(); | ||
|
||
Ok(experiments) | ||
}) | ||
.fold( | ||
Ok(vec![]), | ||
|accum: Result<_, ExperimentError>, value| match (accum, value) { | ||
(Ok(mut accum), Ok(value)) => { | ||
accum.extend(value); | ||
|
||
Ok(accum) | ||
} | ||
(Ok(_), Err(err)) => Err(err), | ||
(Err(accum), Ok(_)) => Err(accum), | ||
(Err(mut accum), Err(err)) => { | ||
accum.extend_one(err); | ||
|
||
Err(accum) | ||
} | ||
}, | ||
) | ||
.attach_printable("unable to set up experiments")?; | ||
|
||
Ok(experiments.iter().map(|experiment| experiment()).collect()) | ||
} | ||
|
||
fn main() -> Result<(), ExperimentError> { | ||
let experiment_ids = &[0, 2, 3]; | ||
let experiment_descriptions = &["10", "20", "3o 4a"]; | ||
start_experiments(experiment_ids, experiment_descriptions)?; | ||
|
||
Ok(()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
#![allow(clippy::print_stdout, clippy::use_debug)] | ||
// This example shows how you can use the `supports-color` and `supports-unicode` crates to | ||
// automatically enable or disable the color mode and set the appropriate charset. | ||
// Your default terminal should automatically show colored unicode output, to emulate a terminal | ||
// that does not support color set your `$TERM` env-variable **temporarily** to "dumb", or set the | ||
// env-variable `NO_COLOR=1`. To emulate no-unicode support set your `$TERM` variable | ||
// **temporarily** to `linux`. | ||
|
||
use core::fmt::{Display, Formatter}; | ||
use std::path::Path; | ||
|
||
use bigerror::{ | ||
error_stack::Result, | ||
fmt::{Charset, ColorMode}, | ||
Report, | ||
}; | ||
|
||
type Config = String; | ||
|
||
#[derive(Debug)] | ||
struct ParseConfigError; | ||
|
||
impl Display for ParseConfigError { | ||
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { | ||
f.write_str("unable to parse config") | ||
} | ||
} | ||
|
||
impl std::error::Error for ParseConfigError {} | ||
|
||
fn parse_config(path: impl AsRef<Path>) -> Result<Config, ParseConfigError> { | ||
_ = path.as_ref(); | ||
|
||
/* | ||
usually you would actually do something here, we just error out, for a more complete example | ||
check out the other examples | ||
*/ | ||
|
||
Err(Report::new(ParseConfigError).attach_printable("unable to read configuration")) | ||
} | ||
|
||
fn main() { | ||
// error-stack only uses ANSI codes for colors | ||
let supports_color = supports_color::on_cached(supports_color::Stream::Stdout) | ||
.map_or(false, |level| level.has_basic); | ||
|
||
let color_mode = if supports_color { | ||
ColorMode::Color | ||
} else { | ||
ColorMode::None | ||
}; | ||
|
||
let supports_unicode = supports_unicode::on(supports_unicode::Stream::Stdout); | ||
|
||
let charset = if supports_unicode { | ||
Charset::Utf8 | ||
} else { | ||
Charset::Ascii | ||
}; | ||
|
||
Report::set_color_mode(color_mode); | ||
Report::set_charset(charset); | ||
|
||
if let Err(err) = parse_config("config.json") { | ||
// if you would use `eprintln!` instead, you should check support on `Stream::Stderr` | ||
// instead. | ||
println!("{err:?}"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
//! Example of using `attach` to set a custom exit code. Requires nightly and std feature. | ||
use std::process::{ExitCode, Termination}; | ||
|
||
use bigerror::{Context, Report}; | ||
|
||
#[derive(Debug)] | ||
struct CustomError; | ||
|
||
impl Context for CustomError {} | ||
|
||
impl core::fmt::Display for CustomError { | ||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
f.write_str("Custom Error") | ||
} | ||
} | ||
|
||
fn main() -> ExitCode { | ||
let report = Report::new(CustomError) | ||
.attach(ExitCode::from(100)) | ||
.attach_printable("this error has an exit code of 100!"); | ||
|
||
report.report() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#![allow(clippy::print_stderr, unreachable_pub, clippy::use_debug)] | ||
// This is the same example also used in `lib.rs`. When updating this, don't forget updating the doc | ||
// example as well. This example is mainly used to generate the output shown in the documentation. | ||
|
||
use core::fmt; | ||
use std::{fs, path::Path}; | ||
|
||
use bigerror::{Context, Report, ResultExt}; | ||
|
||
pub type Config = String; | ||
|
||
#[derive(Debug)] | ||
struct ParseConfigError; | ||
|
||
impl ParseConfigError { | ||
#[must_use] | ||
pub const fn new() -> Self { | ||
Self | ||
} | ||
} | ||
|
||
impl fmt::Display for ParseConfigError { | ||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
fmt.write_str("Could not parse configuration file") | ||
} | ||
} | ||
|
||
impl Context for ParseConfigError {} | ||
|
||
struct Suggestion(&'static str); | ||
|
||
fn parse_config(path: impl AsRef<Path>) -> Result<Config, Report<ParseConfigError>> { | ||
let path = path.as_ref(); | ||
|
||
let content = fs::read_to_string(path) | ||
.change_context(ParseConfigError::new()) | ||
.attach(Suggestion("use a file you can read next time!")) | ||
.attach_printable_lazy(|| format!("could not read file {path:?}"))?; | ||
|
||
Ok(content) | ||
} | ||
|
||
fn main() { | ||
if let Err(report) = parse_config("config.json") { | ||
eprintln!("{report:?}"); | ||
#[cfg(nightly)] | ||
for suggestion in report.request_ref::<Suggestion>() { | ||
eprintln!("Suggestion: {}", suggestion.0); | ||
} | ||
#[cfg(not(nightly))] | ||
while let Some(suggestion) = report.downcast_ref::<Suggestion>() { | ||
eprintln!("Suggestion: {}", suggestion.0); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.