use super::MonadTransformer;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EitherT<M> {
inner: M,
}
impl<M> EitherT<M> {
#[inline]
pub fn new(inner: M) -> Self {
EitherT { inner }
}
#[inline]
pub fn run(self) -> M {
self.inner
}
#[inline]
pub fn inner_ref(&self) -> &M {
&self.inner
}
}
impl<A, E> EitherT<Option<Result<A, E>>> {
#[inline]
pub fn right(value: A) -> Self {
EitherT::new(Some(Ok(value)))
}
#[inline]
pub fn left(error: E) -> Self {
EitherT::new(Some(Err(error)))
}
#[inline]
pub fn absent() -> Self {
EitherT::new(None)
}
#[inline]
pub fn lift_m(option: Option<A>) -> Self {
EitherT::new(option.map(Ok))
}
#[inline]
pub fn map<B, F>(self, f: F) -> EitherT<Option<Result<B, E>>>
where
F: FnOnce(A) -> B,
{
EitherT::new(self.inner.map(|res| res.map(f)))
}
#[inline]
pub fn map_left<E2, F>(self, f: F) -> EitherT<Option<Result<A, E2>>>
where
F: FnOnce(E) -> E2,
{
EitherT::new(self.inner.map(|res| res.map_err(f)))
}
#[inline]
pub fn flat_map<B, F>(self, f: F) -> EitherT<Option<Result<B, E>>>
where
F: FnOnce(A) -> EitherT<Option<Result<B, E>>>,
{
EitherT::new(match self.inner {
Some(Ok(a)) => f(a).inner,
Some(Err(e)) => Some(Err(e)),
None => None,
})
}
#[inline]
pub fn apply<B, F>(self, f: EitherT<Option<Result<F, E>>>) -> EitherT<Option<Result<B, E>>>
where
F: FnOnce(A) -> B,
{
EitherT::new(match (self.inner, f.inner) {
(Some(Ok(a)), Some(Ok(func))) => Some(Ok(func(a))),
(Some(Err(e)), _) | (_, Some(Err(e))) => Some(Err(e)),
(None, _) | (_, None) => None,
})
}
#[inline]
pub fn map2<B, C, F>(
self,
other: EitherT<Option<Result<B, E>>>,
f: F,
) -> EitherT<Option<Result<C, E>>>
where
F: FnOnce(A, B) -> C,
{
EitherT::new(match (self.inner, other.inner) {
(Some(Ok(a)), Some(Ok(b))) => Some(Ok(f(a, b))),
(Some(Err(e)), _) | (_, Some(Err(e))) => Some(Err(e)),
(None, _) | (_, None) => None,
})
}
#[inline]
pub fn handle_error<F>(self, f: F) -> Self
where
F: FnOnce(E) -> Self,
{
EitherT::new(match self.inner {
Some(Ok(a)) => Some(Ok(a)),
Some(Err(e)) => f(e).inner,
None => None,
})
}
#[inline]
pub fn with_error<E2, F>(self, f: F) -> EitherT<Option<Result<A, E2>>>
where
F: FnOnce(E) -> E2,
{
self.map_left(f)
}
#[inline]
pub fn is_right(&self) -> bool {
matches!(&self.inner, Some(Ok(_)))
}
#[inline]
pub fn is_left(&self) -> bool {
matches!(&self.inner, Some(Err(_)))
}
#[inline]
pub fn is_absent(&self) -> bool {
self.inner.is_none()
}
}
impl<A, E> MonadTransformer for EitherT<Option<Result<A, E>>> {
type BaseMonad = Option<A>;
#[inline]
fn lift(base: Option<A>) -> Self {
EitherT::lift_m(base)
}
}
impl<A, E1, E2> EitherT<Result<Result<A, E1>, E2>> {
#[inline]
pub fn right_result(value: A) -> Self {
EitherT::new(Ok(Ok(value)))
}
#[inline]
pub fn left_result(error: E1) -> Self {
EitherT::new(Ok(Err(error)))
}
#[inline]
pub fn outer_err(error: E2) -> Self {
EitherT::new(Err(error))
}
#[inline]
pub fn map_result<B, F>(self, f: F) -> EitherT<Result<Result<B, E1>, E2>>
where
F: FnOnce(A) -> B,
{
EitherT::new(self.inner.map(|res| res.map(f)))
}
#[inline]
pub fn flat_map_result<B, F>(self, f: F) -> EitherT<Result<Result<B, E1>, E2>>
where
F: FnOnce(A) -> EitherT<Result<Result<B, E1>, E2>>,
{
EitherT::new(match self.inner {
Ok(Ok(a)) => f(a).inner,
Ok(Err(e)) => Ok(Err(e)),
Err(e) => Err(e),
})
}
}
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "alloc")]
impl<A, E> EitherT<Vec<Result<A, E>>> {
#[inline]
pub fn right_vec(value: A) -> Self {
EitherT::new(alloc::vec![Ok(value)])
}
#[inline]
pub fn left_vec(error: E) -> Self {
EitherT::new(alloc::vec![Err(error)])
}
#[inline]
pub fn from_vec(values: Vec<Result<A, E>>) -> Self {
EitherT::new(values)
}
#[inline]
pub fn map_vec<B, F>(self, mut f: F) -> EitherT<Vec<Result<B, E>>>
where
F: FnMut(A) -> B,
{
EitherT::new(self.inner.into_iter().map(|res| res.map(&mut f)).collect())
}
#[inline]
pub fn flat_map_vec<B, F>(self, mut f: F) -> EitherT<Vec<Result<B, E>>>
where
F: FnMut(A) -> EitherT<Vec<Result<B, E>>>,
{
let results: Vec<Result<B, E>> = self
.inner
.into_iter()
.flat_map(|res| match res {
Ok(a) => f(a).inner,
Err(e) => alloc::vec![Err(e)],
})
.collect();
EitherT::new(results)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_either_t_right() {
let ok: EitherT<Option<Result<i32, &str>>> = EitherT::right(42);
assert_eq!(ok.run(), Some(Ok(42)));
}
#[test]
fn test_either_t_left() {
let err: EitherT<Option<Result<i32, &str>>> = EitherT::left("error");
assert_eq!(err.run(), Some(Err("error")));
}
#[test]
fn test_either_t_absent() {
let absent: EitherT<Option<Result<i32, &str>>> = EitherT::absent();
assert_eq!(absent.run(), None);
}
#[test]
fn test_either_t_map() {
let ok: EitherT<Option<Result<i32, &str>>> = EitherT::right(21);
let doubled = ok.map(|x| x * 2);
assert_eq!(doubled.run(), Some(Ok(42)));
let err: EitherT<Option<Result<i32, &str>>> = EitherT::left("error");
let err_mapped = err.map(|x| x * 2);
assert_eq!(err_mapped.run(), Some(Err("error")));
}
#[test]
fn test_either_t_flat_map() {
let ok: EitherT<Option<Result<i32, &str>>> = EitherT::right(10);
let result = ok.flat_map(|x| {
if x > 5 {
EitherT::right(x * 2)
} else {
EitherT::left("too small")
}
});
assert_eq!(result.run(), Some(Ok(20)));
let ok2: EitherT<Option<Result<i32, &str>>> = EitherT::right(3);
let result2 = ok2.flat_map(|x| {
if x > 5 {
EitherT::right(x * 2)
} else {
EitherT::left("too small")
}
});
assert_eq!(result2.run(), Some(Err("too small")));
}
#[test]
#[allow(clippy::type_complexity)] fn test_either_t_apply() {
let val: EitherT<Option<Result<i32, &str>>> = EitherT::right(21);
let func: EitherT<Option<Result<fn(i32) -> i32, &str>>> = EitherT::right(|x: i32| x * 2);
let result = val.apply(func);
assert_eq!(result.run(), Some(Ok(42)));
}
#[test]
fn test_either_t_lift_m() {
let some: Option<i32> = Some(42);
let lifted: EitherT<Option<Result<i32, &str>>> = EitherT::lift_m(some);
assert_eq!(lifted.run(), Some(Ok(42)));
let none: Option<i32> = None;
let lifted_none: EitherT<Option<Result<i32, &str>>> = EitherT::lift_m(none);
assert_eq!(lifted_none.run(), None);
}
#[test]
fn test_either_t_handle_error() {
let err: EitherT<Option<Result<i32, &str>>> = EitherT::left("error");
let recovered = err.handle_error(|_| EitherT::right(0));
assert_eq!(recovered.run(), Some(Ok(0)));
}
#[test]
fn test_either_t_left_identity() {
let a = 5;
let f = |x: i32| EitherT::<Option<Result<i32, &str>>>::right(x * 2);
let left = EitherT::<Option<Result<i32, &str>>>::right(a).flat_map(f);
let right = f(a);
assert_eq!(left.run(), right.run());
}
#[test]
fn test_either_t_right_identity() {
let m: EitherT<Option<Result<i32, &str>>> = EitherT::right(42);
let result = m.flat_map(EitherT::right);
assert_eq!(result.run(), Some(Ok(42)));
}
#[test]
fn test_either_t_associativity() {
let m: EitherT<Option<Result<i32, &str>>> = EitherT::right(5);
let f = |x: i32| EitherT::<Option<Result<i32, &str>>>::right(x + 1);
let g = |x: i32| EitherT::<Option<Result<i32, &str>>>::right(x * 2);
let left = m.flat_map(f).flat_map(g);
let right = EitherT::<Option<Result<i32, &str>>>::right(5).flat_map(|x| f(x).flat_map(g));
assert_eq!(left.run(), right.run());
}
#[cfg(feature = "alloc")]
#[test]
fn test_either_t_vec() {
let ok: EitherT<Vec<Result<i32, &str>>> = EitherT::right_vec(42);
assert_eq!(ok.run(), alloc::vec![Ok(42)]);
let mapped: EitherT<Vec<Result<i32, &str>>> = EitherT::right_vec(21);
let result = mapped.map_vec(|x| x * 2);
assert_eq!(result.run(), alloc::vec![Ok(42)]);
}
}