1 unstable release
0.1.0 | Dec 3, 2021 |
---|
#9 in #fatal
22KB
436 lines
Tiered Result
An alternate strategy for error handling.
Checkout the docs on docs.rs.
lib.rs
:
This crate is an alternate strategy for error handling for libraries.
Public functions that might want to keep trying in case of error, or report
multiple errors can take an additional parameter &mut dyn ErrorHandler
. When
they encounter an error that they can sort-of recover from, they can invoke the
error handler (which calls back into the library user's code) and the error
handler gets to decide if the function should keep going or stop early.
When the error handler says to return early, this is considered a fatal error and all further processing and recovery should stop, and the library should return control to the caller as quickly as possibly.
If the error handler says to continue but the error does not allow the
calculation to finish, it should return null. This leads to the four variants in
a TieredResult
: [Ok
], [Err
], Null
, and Fatal
.
Your library code might look something like this:
use tiered_result::TieredResult;
use std::fmt::{Display, Formatter};
pub use tiered_result::{ErrorHandler, ClientResponse};
use nullable_result::NullableResult;
pub fn public_fn(handler: &mut dyn ErrorHandler<Error, Fatality>) -> Option<i64> {
match private_fn(handler) {
TieredResult::Ok(n) => Some(i64::from(n)),
TieredResult::Err(_) => Some(-1),
TieredResult::Null => None,
TieredResult::Fatal(_) => Some(-2)
}
}
fn private_fn(handler: &mut dyn ErrorHandler<Error, Fatality>) -> TieredResult<u32, Error, Fatality> {//!
let n = another_private_fn(handler)?; // <-- this `?` bubbles up the fatal error
// leaving a nullable result behind
let n = n?; // <-- this `?` bubbles up the null/err.
// the previous two lines could be written as let n = another_private_fn(handler)??;
if n == 42 {
handler.handle_error(Error("we don't like 42".to_owned()))?;
}
TieredResult::Ok(n + 5)
}
fn another_private_fn(handler: &mut dyn ErrorHandler<Error, Fatality>) -> TieredResult<u32, Error, Fatality> {
// --snip--
# TieredResult::Ok(2)
}
// a struct to represent fatal errors, can carry additional info if you need it to
struct Fatality;
#[derive(Clone, Debug)]
struct Error(String); // any old error type
impl std::error::Error for Error {}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "There was an error: {}", self.0)
}
}
The calling application might look like this:
use the_lib_from_before::*;
#[derive(Default)]
struct Handler(u8);
impl ErrorHandler<Error, Fatality> for Handler {
fn handle_error(&mut self, error: Error) -> ClientResponse<Fatality, ()> {
if self.0 > 2 { // allow two errors before giving up
ClientResponse::Throw(Fatality)
} else {
ClientResponse::Continue(())
}
}
}
let mut handler = Handler::default();
println!("{:?}", public_fn(&mut handler));
Dependencies
~25KB