Rename and rework the error tools to Flow
This commit is contained in:
parent
c030b2d941
commit
902b690d1a
|
@ -1,7 +0,0 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "errors"
|
||||
version = "0.1.0"
|
|
@ -1,29 +0,0 @@
|
|||
use std::error::Error;
|
||||
use std::result;
|
||||
|
||||
pub trait FatalError {}
|
||||
|
||||
pub type Result<A, FE, E> = result::Result<result::Result<A, E>, FE>;
|
||||
|
||||
pub fn ok<A, FE: FatalError, E: Error>(val: A) -> Result<A, FE, E> {
|
||||
Ok(Ok(val))
|
||||
}
|
||||
|
||||
pub fn error<A, FE: FatalError, E: Error>(err: E) -> Result<A, FE, E> {
|
||||
Ok(Err(err))
|
||||
}
|
||||
|
||||
pub fn fatal<A, FE: FatalError, E: Error>(err: FE) -> Result<A, FE, E> {
|
||||
Err(err)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! result {
|
||||
($x:expr) => {
|
||||
match $x {
|
||||
Ok(Ok(val)) => val,
|
||||
Ok(Err(err)) => return Ok(Err(err.into())),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "flow"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
|
|
@ -1,8 +1,11 @@
|
|||
[package]
|
||||
name = "errors"
|
||||
name = "flow"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
thiserror = "1"
|
|
@ -0,0 +1,217 @@
|
|||
use std::error::Error;
|
||||
|
||||
pub trait FatalError: Error {}
|
||||
|
||||
pub enum Flow<A, FE, E> {
|
||||
Ok(A),
|
||||
Fatal(FE),
|
||||
Err(E),
|
||||
}
|
||||
|
||||
impl<A, FE, E> Flow<A, FE, E> {
|
||||
pub fn map<B, O>(self, mapper: O) -> Flow<B, FE, E>
|
||||
where
|
||||
O: FnOnce(A) -> B,
|
||||
{
|
||||
match self {
|
||||
Flow::Ok(val) => Flow::Ok(mapper(val)),
|
||||
Flow::Fatal(err) => Flow::Fatal(err),
|
||||
Flow::Err(err) => Flow::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn and_then<B, O>(self, handler: O) -> Flow<B, FE, E>
|
||||
where
|
||||
O: FnOnce(A) -> Flow<B, FE, E>,
|
||||
{
|
||||
match self {
|
||||
Flow::Ok(val) => handler(val),
|
||||
Flow::Fatal(err) => Flow::Fatal(err),
|
||||
Flow::Err(err) => Flow::Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_err<F, O>(self, mapper: O) -> Flow<A, FE, F>
|
||||
where
|
||||
O: FnOnce(E) -> F,
|
||||
{
|
||||
match self {
|
||||
Flow::Ok(val) => Flow::Ok(val),
|
||||
Flow::Fatal(err) => Flow::Fatal(err),
|
||||
Flow::Err(err) => Flow::Err(mapper(err)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn or_else<O, F>(self, handler: O) -> Flow<A, FE, F>
|
||||
where
|
||||
O: FnOnce(E) -> Flow<A, FE, F>,
|
||||
{
|
||||
match self {
|
||||
Flow::Ok(val) => Flow::Ok(val),
|
||||
Flow::Fatal(err) => Flow::Fatal(err),
|
||||
Flow::Err(err) => handler(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A, FE, E> From<Result<A, E>> for Flow<A, FE, E> {
|
||||
fn from(r: Result<A, E>) -> Self {
|
||||
match r {
|
||||
Ok(val) => Flow::Ok(val),
|
||||
Err(err) => Flow::Err(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ok<A, FE: FatalError, E: Error>(val: A) -> Flow<A, FE, E> {
|
||||
Flow::Ok(val)
|
||||
}
|
||||
|
||||
pub fn error<A, FE: FatalError, E: Error>(err: E) -> Flow<A, FE, E> {
|
||||
Flow::Err(err)
|
||||
}
|
||||
|
||||
pub fn fatal<A, FE: FatalError, E: Error>(err: FE) -> Flow<A, FE, E> {
|
||||
Flow::Fatal(err)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_fatal {
|
||||
($x:expr) => {
|
||||
match $x {
|
||||
Flow::Fatal(err) => return Flow::Fatal(err),
|
||||
Flow::Err(err) => Err(err),
|
||||
Flow::Ok(val) => Ok(val),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! return_error {
|
||||
($x:expr) => {
|
||||
match $x {
|
||||
Flow::Ok(val) => val,
|
||||
Flow::Err(err) => return Flow::Err(err),
|
||||
Flow::Fatal(err) => return Flow::Fatal(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum FatalError {
|
||||
#[error("A fatal error occurred")]
|
||||
FatalError,
|
||||
}
|
||||
impl super::FatalError for FatalError {}
|
||||
impl PartialEq for FatalError {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
enum Error {
|
||||
#[error("an error occurred")]
|
||||
Error,
|
||||
}
|
||||
impl PartialEq for Error {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Flow<i32, FatalError, Error> {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
match (self, rhs) {
|
||||
(Flow::Ok(val), Flow::Ok(rhs)) => val == rhs,
|
||||
(Flow::Err(_), Flow::Err(_)) => true,
|
||||
(Flow::Fatal(_), Flow::Fatal(_)) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Flow<i32, FatalError, Error> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Flow::Ok(val) => f.write_fmt(format_args!("Flow::Ok {}", val)),
|
||||
Flow::Err(err) => f.write_fmt(format_args!("Flow::Err {:?}", err)),
|
||||
Flow::Fatal(err) => f.write_fmt(format_args!("Flow::Fatal {:?}", err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_map_things() {
|
||||
let success = ok(15);
|
||||
assert_eq!(ok(16), success.map(|v| v + 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_chain_success() {
|
||||
let success = ok(15);
|
||||
assert_eq!(ok(16), success.and_then(|v| ok(v + 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_handle_an_error() {
|
||||
let failure = error(Error::Error);
|
||||
assert_eq!(ok(16), failure.or_else(|_| ok(16)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn early_exit_on_fatal() {
|
||||
fn ok_func() -> Flow<i32, FatalError, Error> {
|
||||
let value = return_fatal!(ok::<i32, FatalError, Error>(15));
|
||||
match value {
|
||||
Ok(_) => ok(14),
|
||||
Err(err) => error(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn err_func() -> Flow<i32, FatalError, Error> {
|
||||
let value = return_fatal!(error::<i32, FatalError, Error>(Error::Error));
|
||||
match value {
|
||||
Ok(_) => panic!("shouldn't have gotten here"),
|
||||
Err(err) => ok(0),
|
||||
}
|
||||
}
|
||||
|
||||
fn fatal_func() -> Flow<i32, FatalError, Error> {
|
||||
return_fatal!(fatal::<i32, FatalError, Error>(FatalError::FatalError));
|
||||
panic!("failed to bail");
|
||||
}
|
||||
|
||||
fatal_func();
|
||||
assert_eq!(ok_func(), ok(14));
|
||||
assert_eq!(err_func(), ok(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_can_early_exit_on_all_errors() {
|
||||
fn ok_func() -> Flow<i32, FatalError, Error> {
|
||||
let value = return_error!(ok::<i32, FatalError, Error>(15));
|
||||
assert_eq!(value, 15);
|
||||
ok(14)
|
||||
}
|
||||
|
||||
fn err_func() -> Flow<i32, FatalError, Error> {
|
||||
return_error!(error::<i32, FatalError, Error>(Error::Error));
|
||||
panic!("failed to bail");
|
||||
}
|
||||
|
||||
fn fatal_func() -> Flow<i32, FatalError, Error> {
|
||||
return_error!(fatal::<i32, FatalError, Error>(FatalError::FatalError));
|
||||
panic!("failed to bail");
|
||||
}
|
||||
|
||||
fatal_func();
|
||||
assert_eq!(ok_func(), ok(14));
|
||||
err_func();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue