Compare commits

...

2 Commits

Author SHA1 Message Date
Savanni D'Gerinel b756e8ca81 Reverse the order of Error and FatalError parameters in the Result
In other usage, I discovered that it's rather confusing to have the parameters in the order that they were in. It feels better to have the fatal error after the regular error.
2023-10-18 22:13:11 -04:00
Savanni D'Gerinel 3cb742d863 Rename flow to result-extended
The original name has always felt awful. I understand Rust well enough now to be able to use the name Result and override the built-in Result.
2023-10-18 22:03:43 -04:00
7 changed files with 78 additions and 77 deletions

14
Cargo.lock generated
View File

@ -861,13 +861,6 @@ dependencies = [
"miniz_oxide 0.7.1", "miniz_oxide 0.7.1",
] ]
[[package]]
name = "flow"
version = "0.1.0"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "fluent" name = "fluent"
version = "0.16.0" version = "0.16.0"
@ -3119,6 +3112,13 @@ dependencies = [
"winreg", "winreg",
] ]
[[package]]
name = "result-extended"
version = "0.1.0"
dependencies = [
"thiserror",
]
[[package]] [[package]]
name = "rsa" name = "rsa"
version = "0.9.2" version = "0.9.2"

View File

@ -8,7 +8,6 @@ members = [
"dashboard", "dashboard",
"emseries", "emseries",
"file-service", "file-service",
"flow",
"fluent-ergonomics", "fluent-ergonomics",
"geo-types", "geo-types",
"gm-control-panel", "gm-control-panel",
@ -17,7 +16,8 @@ members = [
"kifu/core", "kifu/core",
"kifu/gtk", "kifu/gtk",
"memorycache", "memorycache",
"nom-training",
"result-extended",
"screenplay", "screenplay",
"sgf", "sgf",
"nom-training",
] ]

View File

@ -11,7 +11,6 @@ RUST_ALL_TARGETS=(
"dashboard" "dashboard"
"emseries" "emseries"
"file-service" "file-service"
"flow"
"fluent-ergonomics" "fluent-ergonomics"
"geo-types" "geo-types"
"gm-control-panel" "gm-control-panel"
@ -20,6 +19,8 @@ RUST_ALL_TARGETS=(
"kifu-core" "kifu-core"
"kifu-gtk" "kifu-gtk"
"memorycache" "memorycache"
"nom-training"
"result-extended"
"screenplay" "screenplay"
"sgf" "sgf"
) )

View File

@ -1,5 +1,5 @@
[package] [package]
name = "flow" name = "result-extended"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
license = "GPL-3.0-only" license = "GPL-3.0-only"

View File

@ -33,84 +33,84 @@ use std::{error::Error, fmt};
/// statement. /// 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 /// Result<A, FE, E> represents a return value that might be a success, might be a fatal error, or
/// might be a normal handleable error. /// might be a normal handleable error.
pub enum Flow<A, FE, E> { pub enum Result<A, E, FE> {
/// The operation was successful /// The operation was successful
Ok(A), Ok(A),
/// Ordinary errors. These should be handled and the application should recover gracefully.
Err(E),
/// The operation encountered a fatal error. These should be bubbled up to a level that can /// The operation encountered a fatal error. These should be bubbled up to a level that can
/// safely shut the application down. /// safely shut the application down.
Fatal(FE), Fatal(FE),
/// Ordinary errors. These should be handled and the application should recover gracefully.
Err(E),
} }
impl<A, FE, E> Flow<A, FE, E> { impl<A, E, FE> Result<A, E, FE> {
/// Apply an infallible function to a successful value. /// 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) -> Result<B, E, FE>
where where
O: FnOnce(A) -> B, O: FnOnce(A) -> B,
{ {
match self { match self {
Flow::Ok(val) => Flow::Ok(mapper(val)), Result::Ok(val) => Result::Ok(mapper(val)),
Flow::Fatal(err) => Flow::Fatal(err), Result::Err(err) => Result::Err(err),
Flow::Err(err) => Flow::Err(err), Result::Fatal(err) => Result::Fatal(err),
} }
} }
/// Apply a potentially fallible function to a successful value. /// Apply a potentially fallible function to a successful value.
/// ///
/// Like `Result.and_then`, the mapping function can itself fail. /// 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) -> Result<B, E, FE>
where where
O: FnOnce(A) -> Flow<B, FE, E>, O: FnOnce(A) -> Result<B, E, FE>,
{ {
match self { match self {
Flow::Ok(val) => handler(val), Result::Ok(val) => handler(val),
Flow::Fatal(err) => Flow::Fatal(err), Result::Err(err) => Result::Err(err),
Flow::Err(err) => Flow::Err(err), Result::Fatal(err) => Result::Fatal(err),
} }
} }
/// Map a normal error from one type to another. This is useful for converting an error from /// 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 /// 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. /// work with `Result`, 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) -> Result<A, F, FE>
where where
O: FnOnce(E) -> F, O: FnOnce(E) -> F,
{ {
match self { match self {
Flow::Ok(val) => Flow::Ok(val), Result::Ok(val) => Result::Ok(val),
Flow::Fatal(err) => Flow::Fatal(err), Result::Err(err) => Result::Err(mapper(err)),
Flow::Err(err) => Flow::Err(mapper(err)), Result::Fatal(err) => Result::Fatal(err),
} }
} }
/// Provide a function to use to recover from (or simply re-throw) an error. /// 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) -> Result<A, F, FE>
where where
O: FnOnce(E) -> Flow<A, FE, F>, O: FnOnce(E) -> Result<A, F, FE>,
{ {
match self { match self {
Flow::Ok(val) => Flow::Ok(val), Result::Ok(val) => Result::Ok(val),
Flow::Fatal(err) => Flow::Fatal(err), Result::Err(err) => handler(err),
Flow::Err(err) => handler(err), Result::Fatal(err) => Result::Fatal(err),
} }
} }
} }
/// Convert from a normal `Result` type to a `Flow` type. The error condition for a `Result` will /// Convert from a normal `Result` type to a `Result` type. The error condition for a `Result` will
/// be treated as `Flow::Err`, never `Flow::Fatal`. /// be treated as `Result::Err`, never `Result::Fatal`.
impl<A, FE, E> From<Result<A, E>> for Flow<A, FE, E> { impl<A, E, FE> From<std::result::Result<A, E>> for Result<A, E, FE> {
fn from(r: Result<A, E>) -> Self { fn from(r: std::result::Result<A, E>) -> Self {
match r { match r {
Ok(val) => Flow::Ok(val), Ok(val) => Result::Ok(val),
Err(err) => Flow::Err(err), Err(err) => Result::Err(err),
} }
} }
} }
impl<A, FE, E> fmt::Debug for Flow<A, FE, E> impl<A, E, FE> fmt::Debug for Result<A, E, FE>
where where
A: fmt::Debug, A: fmt::Debug,
FE: fmt::Debug, FE: fmt::Debug,
@ -118,14 +118,14 @@ where
{ {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Flow::Ok(val) => f.write_fmt(format_args!("Flow::Ok {:?}", val)), Result::Ok(val) => f.write_fmt(format_args!("Result::Ok {:?}", val)),
Flow::Err(err) => f.write_fmt(format_args!("Flow::Err {:?}", err)), Result::Err(err) => f.write_fmt(format_args!("Result::Err {:?}", err)),
Flow::Fatal(err) => f.write_fmt(format_args!("Flow::Fatal {:?}", err)), Result::Fatal(err) => f.write_fmt(format_args!("Result::Fatal {:?}", err)),
} }
} }
} }
impl<A, FE, E> PartialEq for Flow<A, FE, E> impl<A, E, FE> PartialEq for Result<A, E, FE>
where where
A: PartialEq, A: PartialEq,
FE: PartialEq, FE: PartialEq,
@ -133,27 +133,27 @@ where
{ {
fn eq(&self, rhs: &Self) -> bool { fn eq(&self, rhs: &Self) -> bool {
match (self, rhs) { match (self, rhs) {
(Flow::Ok(val), Flow::Ok(rhs)) => val == rhs, (Result::Ok(val), Result::Ok(rhs)) => val == rhs,
(Flow::Err(_), Flow::Err(_)) => true, (Result::Err(_), Result::Err(_)) => true,
(Flow::Fatal(_), Flow::Fatal(_)) => true, (Result::Fatal(_), Result::Fatal(_)) => true,
_ => false, _ => false,
} }
} }
} }
/// Convenience function to create an ok value. /// 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, E: Error, FE: FatalError>(val: A) -> Result<A, E, FE> {
Flow::Ok(val) Result::Ok(val)
} }
/// Convenience function to create an error value. /// 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, E: Error, FE: FatalError>(err: E) -> Result<A, E, FE> {
Flow::Err(err) Result::Err(err)
} }
/// Convenience function to create a fatal value. /// 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, E: Error, FE: FatalError>(err: FE) -> Result<A, E, FE> {
Flow::Fatal(err) Result::Fatal(err)
} }
/// Return early from the current function if the value is a fatal error. /// Return early from the current function if the value is a fatal error.
@ -161,9 +161,9 @@ pub fn fatal<A, FE: FatalError, E: Error>(err: FE) -> Flow<A, FE, E> {
macro_rules! return_fatal { macro_rules! return_fatal {
($x:expr) => { ($x:expr) => {
match $x { match $x {
Flow::Fatal(err) => return Flow::Fatal(err), Result::Fatal(err) => return Result::Fatal(err),
Flow::Err(err) => Err(err), Result::Err(err) => Err(err),
Flow::Ok(val) => Ok(val), Result::Ok(val) => Ok(val),
} }
}; };
} }
@ -173,9 +173,9 @@ macro_rules! return_fatal {
macro_rules! return_error { macro_rules! return_error {
($x:expr) => { ($x:expr) => {
match $x { match $x {
Flow::Ok(val) => val, Result::Ok(val) => val,
Flow::Err(err) => return Flow::Err(err), Result::Err(err) => return Result::Err(err),
Flow::Fatal(err) => return Flow::Fatal(err), Result::Fatal(err) => return Result::Fatal(err),
} }
}; };
} }
@ -210,45 +210,45 @@ mod test {
#[test] #[test]
fn it_can_map_things() { fn it_can_map_things() {
let success: Flow<i32, FatalError, Error> = ok(15); let success: Result<i32, Error, FatalError> = ok(15);
assert_eq!(ok(16), success.map(|v| v + 1)); assert_eq!(ok(16), success.map(|v| v + 1));
} }
#[test] #[test]
fn it_can_chain_success() { fn it_can_chain_success() {
let success: Flow<i32, FatalError, Error> = ok(15); let success: Result<i32, Error, FatalError> = ok(15);
assert_eq!(ok(16), success.and_then(|v| ok(v + 1))); assert_eq!(ok(16), success.and_then(|v| ok(v + 1)));
} }
#[test] #[test]
fn it_can_handle_an_error() { fn it_can_handle_an_error() {
let failure: Flow<i32, FatalError, Error> = error(Error::Error); let failure: Result<i32, Error, FatalError> = error(Error::Error);
assert_eq!( assert_eq!(
ok::<i32, FatalError, Error>(16), ok::<i32, Error, FatalError>(16),
failure.or_else(|_| ok(16)) failure.or_else(|_| ok(16))
); );
} }
#[test] #[test]
fn early_exit_on_fatal() { fn early_exit_on_fatal() {
fn ok_func() -> Flow<i32, FatalError, Error> { fn ok_func() -> Result<i32, Error, FatalError> {
let value = return_fatal!(ok::<i32, FatalError, Error>(15)); let value = return_fatal!(ok::<i32, Error, FatalError>(15));
match value { match value {
Ok(_) => ok(14), Ok(_) => ok(14),
Err(err) => error(err), Err(err) => error(err),
} }
} }
fn err_func() -> Flow<i32, FatalError, Error> { fn err_func() -> Result<i32, Error, FatalError> {
let value = return_fatal!(error::<i32, FatalError, Error>(Error::Error)); let value = return_fatal!(error::<i32, Error, FatalError>(Error::Error));
match value { match value {
Ok(_) => panic!("shouldn't have gotten here"), Ok(_) => panic!("shouldn't have gotten here"),
Err(_) => ok(0), Err(_) => ok(0),
} }
} }
fn fatal_func() -> Flow<i32, FatalError, Error> { fn fatal_func() -> Result<i32, Error, FatalError> {
return_fatal!(fatal::<i32, FatalError, Error>(FatalError::FatalError)); let _ = return_fatal!(fatal::<i32, Error, FatalError>(FatalError::FatalError));
panic!("failed to bail"); panic!("failed to bail");
} }
@ -259,19 +259,19 @@ mod test {
#[test] #[test]
fn it_can_early_exit_on_all_errors() { fn it_can_early_exit_on_all_errors() {
fn ok_func() -> Flow<i32, FatalError, Error> { fn ok_func() -> Result<i32, Error, FatalError> {
let value = return_error!(ok::<i32, FatalError, Error>(15)); let value = return_error!(ok::<i32, Error, FatalError>(15));
assert_eq!(value, 15); assert_eq!(value, 15);
ok(14) ok(14)
} }
fn err_func() -> Flow<i32, FatalError, Error> { fn err_func() -> Result<i32, Error, FatalError> {
return_error!(error::<i32, FatalError, Error>(Error::Error)); return_error!(error::<i32, Error, FatalError>(Error::Error));
panic!("failed to bail"); panic!("failed to bail");
} }
fn fatal_func() -> Flow<i32, FatalError, Error> { fn fatal_func() -> Result<i32, Error, FatalError> {
return_error!(fatal::<i32, FatalError, Error>(FatalError::FatalError)); return_error!(fatal::<i32, Error, FatalError>(FatalError::FatalError));
panic!("failed to bail"); panic!("failed to bail");
} }