use core::marker::PhantomData;
use crate::nexus::effect::{Eff, EffectMarker};
use crate::nexus::row::{ERROR_BIT, Row};
#[derive(Copy, Clone, Debug)]
pub struct ErrorEffect<E> {
_marker: PhantomData<E>,
}
impl<E> EffectMarker for ErrorEffect<E> {
const BIT: u128 = ERROR_BIT;
const NAME: &'static str = "Error";
}
pub type ErrorRow = Row<ERROR_BIT>;
pub enum ErrorOp<E> {
Throw(E),
Catch,
}
pub fn error_throw<E: 'static, A: 'static>(_error: E) -> Eff<ErrorRow, A> {
Eff::lazy(|| crate::cold_panic!("error_throw requires Error handler"))
}
pub fn error_ok<E: 'static, A: 'static>(value: A) -> Eff<ErrorRow, A> {
Eff::from_value(value)
}
#[must_use = "error computations do nothing unless run"]
#[repr(transparent)]
pub struct ErrorComputation<E, A> {
result: Result<A, E>,
}
impl<E, A> ErrorComputation<E, A> {
#[inline(always)]
pub fn ok(value: A) -> Self {
ErrorComputation { result: Ok(value) }
}
#[inline(always)]
pub fn err(error: E) -> Self {
ErrorComputation { result: Err(error) }
}
#[inline(always)]
pub fn from_result(result: Result<A, E>) -> Self {
ErrorComputation { result }
}
#[inline(always)]
pub fn run(self) -> Result<A, E> {
self.result
}
#[inline(always)]
pub fn map<B, F: FnOnce(A) -> B>(self, f: F) -> ErrorComputation<E, B> {
ErrorComputation {
result: self.result.map(f),
}
}
#[inline(always)]
pub fn map_err<E2, F: FnOnce(E) -> E2>(self, f: F) -> ErrorComputation<E2, A> {
ErrorComputation {
result: self.result.map_err(f),
}
}
#[inline(always)]
pub fn and_then<B, F: FnOnce(A) -> ErrorComputation<E, B>>(
self,
f: F,
) -> ErrorComputation<E, B> {
match self.result {
Ok(a) => f(a),
Err(e) => ErrorComputation::err(e),
}
}
#[inline(always)]
pub fn or_else<F: FnOnce(E) -> ErrorComputation<E, A>>(self, f: F) -> ErrorComputation<E, A> {
match self.result {
Ok(a) => ErrorComputation::ok(a),
Err(e) => f(e),
}
}
#[inline]
pub fn unwrap_or(self, default: A) -> A {
self.result.unwrap_or(default)
}
#[inline]
pub fn unwrap_or_else<F: FnOnce(E) -> A>(self, f: F) -> A {
self.result.unwrap_or_else(f)
}
#[inline]
pub fn ok_option(self) -> Option<A> {
self.result.ok()
}
#[inline]
pub fn err_option(self) -> Option<E> {
self.result.err()
}
}
impl<E, A> From<Result<A, E>> for ErrorComputation<E, A> {
fn from(result: Result<A, E>) -> Self {
ErrorComputation::from_result(result)
}
}
impl<E, A> From<ErrorComputation<E, A>> for Result<A, E> {
fn from(comp: ErrorComputation<E, A>) -> Self {
comp.run()
}
}
pub fn first_success<E, A>(
computations: alloc::vec::Vec<ErrorComputation<E, A>>,
) -> ErrorComputation<alloc::vec::Vec<E>, A> {
let mut errors = alloc::vec::Vec::with_capacity(computations.len());
for comp in computations {
match comp.run() {
Ok(a) => return ErrorComputation::ok(a),
Err(e) => errors.push(e),
}
}
ErrorComputation::err(errors)
}
pub fn sequence_results<E, A>(
computations: alloc::vec::Vec<ErrorComputation<E, A>>,
) -> ErrorComputation<E, alloc::vec::Vec<A>> {
let mut results = alloc::vec::Vec::with_capacity(computations.len());
for comp in computations {
match comp.run() {
Ok(a) => results.push(a),
Err(e) => return ErrorComputation::err(e),
}
}
ErrorComputation::ok(results)
}
pub fn partition_results<E, A>(
computations: alloc::vec::Vec<ErrorComputation<E, A>>,
) -> (alloc::vec::Vec<A>, alloc::vec::Vec<E>) {
let half = computations.len() / 2 + 1;
let mut successes = alloc::vec::Vec::with_capacity(half);
let mut failures = alloc::vec::Vec::with_capacity(half);
for comp in computations {
match comp.run() {
Ok(a) => successes.push(a),
Err(e) => failures.push(e),
}
}
(successes, failures)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_ok() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::ok(42);
assert_eq!(comp.run(), Ok(42));
}
#[test]
fn test_error_err() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::err("oops");
assert_eq!(comp.run(), Err("oops"));
}
#[test]
fn test_error_map() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::ok(21);
let mapped = comp.map(|x| x * 2);
assert_eq!(mapped.run(), Ok(42));
}
#[test]
fn test_error_map_err() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::err("oops");
let mapped = comp.map_err(str::len);
assert_eq!(mapped.run(), Err(4));
}
#[test]
fn test_error_and_then_ok() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::ok(21);
let chained = comp.and_then(|x| ErrorComputation::ok(x * 2));
assert_eq!(chained.run(), Ok(42));
}
#[test]
fn test_error_and_then_err() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::err("first");
let chained = comp.and_then(|x| ErrorComputation::ok(x * 2));
assert_eq!(chained.run(), Err("first"));
}
#[test]
fn test_error_or_else() {
let comp: ErrorComputation<&str, i32> = ErrorComputation::err("oops");
let recovered = comp.or_else(|_| ErrorComputation::ok(42));
assert_eq!(recovered.run(), Ok(42));
}
#[test]
fn test_error_unwrap_or() {
let err_comp: ErrorComputation<&str, i32> = ErrorComputation::err("oops");
assert_eq!(err_comp.unwrap_or(42), 42);
let ok_comp: ErrorComputation<&str, i32> = ErrorComputation::ok(100);
assert_eq!(ok_comp.unwrap_or(42), 100);
}
#[test]
fn test_first_success() {
let comps = alloc::vec![
ErrorComputation::err("a"),
ErrorComputation::ok(42),
ErrorComputation::err("b"),
];
let result = first_success(comps);
assert_eq!(result.run(), Ok(42));
}
#[test]
fn test_first_success_all_fail() {
let comps: alloc::vec::Vec<ErrorComputation<&str, i32>> =
alloc::vec![ErrorComputation::err("a"), ErrorComputation::err("b"),];
let result = first_success(comps);
assert_eq!(result.run(), Err(alloc::vec!["a", "b"]));
}
#[test]
fn test_sequence_results() {
let comps: alloc::vec::Vec<ErrorComputation<&str, i32>> = alloc::vec![
ErrorComputation::ok(1),
ErrorComputation::ok(2),
ErrorComputation::ok(3),
];
let result = sequence_results(comps);
assert_eq!(result.run(), Ok(alloc::vec![1, 2, 3]));
}
#[test]
fn test_partition_results() {
let comps = alloc::vec![
ErrorComputation::ok(1),
ErrorComputation::err("a"),
ErrorComputation::ok(2),
];
let (ok, err) = partition_results(comps);
assert_eq!(ok, alloc::vec![1, 2]);
assert_eq!(err, alloc::vec!["a"]);
}
}