Add a lot of documentation to the Flow library
This commit is contained in:
parent
c76fccb42e
commit
f96a642cf0
|
@ -0,0 +1,3 @@
|
||||||
|
# Flow
|
||||||
|
|
||||||
|
This is a library to help with application control flow, separating fatal errors from normal recoverable errors. This is almost entirely inspired by [Error Handling in a Correctness-Critical Rust Project](https://sled.rs/errors.html) in the sled.rs library.
|
|
@ -1,14 +1,40 @@
|
||||||
|
//! Control Flow for Correctness-Critical applications
|
||||||
|
//!
|
||||||
|
//! https://sled.rs/errors.html
|
||||||
|
//!
|
||||||
|
//! Where the sled.rs library uses `Result<Result<A, Error>, FatalError>`, these are a little hard to
|
||||||
|
//! work with. This library works out a set of utility functions that allow us to work with the
|
||||||
|
//! nested errors in the same way as a regular Result.
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
|
/// Implement this trait for the application's fatal errors.
|
||||||
|
///
|
||||||
|
/// Fatal errors should be things that should trigger an application shutdown. Applications should
|
||||||
|
/// not try to recover from fatal errors, but simply bring the app to the safest shutdown point and
|
||||||
|
/// report the best possible information to the user.
|
||||||
|
///
|
||||||
|
/// Examples: database corruption, or the database is unavailable in an application that cannot
|
||||||
|
/// function without it. Graphics environment cannot be initialized in a GUI app.
|
||||||
|
///
|
||||||
|
/// Applications should generally have only one FatalError type. There are no handling utilities
|
||||||
|
/// for Fatal conditions, so Fatal conditions must be handled through an ordinary `match`
|
||||||
|
/// statement.
|
||||||
pub trait FatalError: Error {}
|
pub trait FatalError: Error {}
|
||||||
|
|
||||||
|
/// Flow<A, FE, E> represents a return value that might be a success, might be a fatal error, or
|
||||||
|
/// might be a normal handleable error.
|
||||||
pub enum Flow<A, FE, E> {
|
pub enum Flow<A, FE, E> {
|
||||||
|
/// The operation was successful
|
||||||
Ok(A),
|
Ok(A),
|
||||||
|
/// The operation encountered a fatal error. These should be bubbled up to a level that can
|
||||||
|
/// safely shut the application down.
|
||||||
Fatal(FE),
|
Fatal(FE),
|
||||||
|
/// Ordinary errors. These should be handled and the application should recover gracefully.
|
||||||
Err(E),
|
Err(E),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A, FE, E> Flow<A, FE, E> {
|
impl<A, FE, E> Flow<A, FE, E> {
|
||||||
|
/// Apply an infallible function to a successful value.
|
||||||
pub fn map<B, O>(self, mapper: O) -> Flow<B, FE, E>
|
pub fn map<B, O>(self, mapper: O) -> Flow<B, FE, E>
|
||||||
where
|
where
|
||||||
O: FnOnce(A) -> B,
|
O: FnOnce(A) -> B,
|
||||||
|
@ -20,6 +46,9 @@ impl<A, FE, E> Flow<A, FE, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply a potentially fallible function to a successful value.
|
||||||
|
///
|
||||||
|
/// Like `Result.and_then`, the mapping function can itself fail.
|
||||||
pub fn and_then<B, O>(self, handler: O) -> Flow<B, FE, E>
|
pub fn and_then<B, O>(self, handler: O) -> Flow<B, FE, E>
|
||||||
where
|
where
|
||||||
O: FnOnce(A) -> Flow<B, FE, E>,
|
O: FnOnce(A) -> Flow<B, FE, E>,
|
||||||
|
@ -31,6 +60,9 @@ impl<A, FE, E> Flow<A, FE, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Map a normal error from one type to another. This is useful for converting an error from
|
||||||
|
/// one type to another, especially in re-throwing an underlying error. `?` syntax does not
|
||||||
|
/// work with `Flow`, so you will likely need to use this a lot.
|
||||||
pub fn map_err<F, O>(self, mapper: O) -> Flow<A, FE, F>
|
pub fn map_err<F, O>(self, mapper: O) -> Flow<A, FE, F>
|
||||||
where
|
where
|
||||||
O: FnOnce(E) -> F,
|
O: FnOnce(E) -> F,
|
||||||
|
@ -42,6 +74,7 @@ impl<A, FE, E> Flow<A, FE, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Provide a function to use to recover from (or simply re-throw) an error.
|
||||||
pub fn or_else<O, F>(self, handler: O) -> Flow<A, FE, F>
|
pub fn or_else<O, F>(self, handler: O) -> Flow<A, FE, F>
|
||||||
where
|
where
|
||||||
O: FnOnce(E) -> Flow<A, FE, F>,
|
O: FnOnce(E) -> Flow<A, FE, F>,
|
||||||
|
@ -54,6 +87,8 @@ impl<A, FE, E> Flow<A, FE, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert from a normal `Result` type to a `Flow` type. The error condition for a `Result` will
|
||||||
|
/// be treated as `Flow::Err`, never `Flow::Fatal`.
|
||||||
impl<A, FE, E> From<Result<A, E>> for Flow<A, FE, E> {
|
impl<A, FE, E> From<Result<A, E>> for Flow<A, FE, E> {
|
||||||
fn from(r: Result<A, E>) -> Self {
|
fn from(r: Result<A, E>) -> Self {
|
||||||
match r {
|
match r {
|
||||||
|
@ -63,19 +98,23 @@ impl<A, FE, E> From<Result<A, E>> for Flow<A, FE, E> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function to create an ok value.
|
||||||
pub fn ok<A, FE: FatalError, E: Error>(val: A) -> Flow<A, FE, E> {
|
pub fn ok<A, FE: FatalError, E: Error>(val: A) -> Flow<A, FE, E> {
|
||||||
Flow::Ok(val)
|
Flow::Ok(val)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function to create an error value.
|
||||||
pub fn error<A, FE: FatalError, E: Error>(err: E) -> Flow<A, FE, E> {
|
pub fn error<A, FE: FatalError, E: Error>(err: E) -> Flow<A, FE, E> {
|
||||||
Flow::Err(err)
|
Flow::Err(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function to create a fatal value.
|
||||||
pub fn fatal<A, FE: FatalError, E: Error>(err: FE) -> Flow<A, FE, E> {
|
pub fn fatal<A, FE: FatalError, E: Error>(err: FE) -> Flow<A, FE, E> {
|
||||||
Flow::Fatal(err)
|
Flow::Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
/// Return early from the current function if the value is a fatal error.
|
||||||
macro_rules! return_fatal {
|
macro_rules! return_fatal {
|
||||||
($x:expr) => {
|
($x:expr) => {
|
||||||
match $x {
|
match $x {
|
||||||
|
@ -87,6 +126,7 @@ macro_rules! return_fatal {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
/// Return early from the current function is the value is an error.
|
||||||
macro_rules! return_error {
|
macro_rules! return_error {
|
||||||
($x:expr) => {
|
($x:expr) => {
|
||||||
match $x {
|
match $x {
|
||||||
|
|
Loading…
Reference in New Issue