use std::ops::{ControlFlow, FromResidual, Residual, Try};
pub trait Transform<T>
where
Self: Try<Output = T> + Sized,
{
fn flatten(self) -> T
where
T: FromResidual<Self::Residual>,
{
self?
}
fn inspect<F>(self, f: F) -> Self
where
F: FnOnce(&T),
{
let val = self?;
f(&val);
Try::from_output(val)
}
fn map<X, U, F>(self, f: F) -> X
where
F: FnOnce(T) -> U,
X: Try<Output = U> + FromResidual<Self::Residual>,
Self::Residual: Residual<U, TryType = X>,
{
let val = self?;
let mapped = f(val);
Try::from_output(mapped)
}
fn map_or<U, F>(self, default: U, f: F) -> U
where
F: FnOnce(T) -> U,
{
match self.branch() {
ControlFlow::Continue(val) => f(val),
ControlFlow::Break(_) => default,
}
}
fn map_or_else<U, D, F>(self, default: D, f: F) -> U
where
D: FnOnce() -> U,
F: FnOnce(T) -> U,
{
match self.branch() {
ControlFlow::Continue(val) => f(val),
ControlFlow::Break(_) => default(),
}
}
fn transpose<X>(self) -> X
where
T: Try,
X: Try + FromResidual<T::Residual>,
X::Output: Try<Output = T::Output> + FromResidual<Self::Residual>,
T::Residual: Residual<X::Output, TryType = X>,
Self::Residual: Residual<T::Output, TryType = X::Output>,
{
match self.branch() {
ControlFlow::Continue(inner_u) => match inner_u.branch() {
ControlFlow::Continue(val) => {
let inner_t = Try::from_output(val);
Try::from_output(inner_t)
}
ControlFlow::Break(u_residual) => FromResidual::from_residual(u_residual),
},
ControlFlow::Break(t_residual) => {
let inner_t = FromResidual::from_residual(t_residual);
Try::from_output(inner_t)
}
}
}
fn zip<X, Y>(self, other: Y) -> X
where
Y: Try,
X: Try<Output = (T, Y::Output)> + FromResidual<Self::Residual> + FromResidual<Y::Residual>,
Self::Residual: Residual<X::Output, TryType = X>,
{
let v1 = self?;
let v2 = other?;
Try::from_output((v1, v2))
}
fn zip_with<X, Y, F, G>(self, other: Y, f: F) -> X
where
Y: Try,
F: FnOnce(T, Y::Output) -> G,
X: Try<Output = G> + FromResidual<Self::Residual> + FromResidual<Y::Residual>,
Self::Residual: Residual<G, TryType = X>,
{
let v1 = self?;
let v2 = other?;
Try::from_output(f(v1, v2))
}
fn and<Y>(self, other: Y) -> Y
where
Y: Try<Output = T> + FromResidual<Self::Residual>,
{
self?;
other
}
fn and_then<Y, F>(self, f: F) -> Y
where
Y: Try<Output = T> + FromResidual<Self::Residual>,
F: FnOnce(T) -> Y,
{
f(self?)
}
fn or<Y>(self, other: Y) -> Y
where
Y: Try<Output = T>,
{
match self.branch() {
ControlFlow::Continue(v) => Try::from_output(v),
ControlFlow::Break(_) => other,
}
}
fn or_else<Y, F>(self, f: F) -> Y
where
Y: Try<Output = T>,
F: FnOnce(Self::Residual) -> Y,
{
match self.branch() {
ControlFlow::Continue(v) => Try::from_output(v),
ControlFlow::Break(r) => f(r),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
impl<T> Transform<T> for Option<T> {}
impl<T, E> Transform<T> for Result<T, E> {}
mod boolean {
use super::*;
#[test]
fn and_some_some() {
let some_5 = Some(5);
let some_6 = Some(6);
let stdlib = some_5.and(some_6);
let custom = Transform::and(some_5, some_6);
assert_eq!(stdlib, custom);
}
#[test]
fn and_none_some() {
let some_5 = None;
let some_6 = Some(6);
let stdlib = some_5.and(some_6);
let custom = Transform::and(some_5, some_6);
assert_eq!(stdlib, custom);
}
#[test]
fn and_some_none() {
let some_5 = Some(5);
let some_6 = None;
let stdlib = some_5.and(some_6);
let custom = Transform::and(some_5, some_6);
assert_eq!(stdlib, custom);
}
#[test]
fn or_ok_ok() {
let ok_5: Result<_, ()> = Ok(5);
let ok_6: Result<_, i32> = Ok(6);
let stdlib = ok_5.or(ok_6);
let custom = Transform::or(ok_5, ok_6);
assert_eq!(stdlib, custom);
}
#[test]
fn or_ok_some() {
let ok_5: Result<_, ()> = Ok(5);
let some_6 = Some(6);
let custom = Transform::or(ok_5, some_6);
assert_eq!(custom, Some(5));
}
}
mod flatten {
use super::*;
#[test]
fn some_some() {
let some_5 = Some(Some(5));
let stdlib = some_5.flatten();
let custom = Transform::flatten(some_5);
assert_eq!(stdlib, custom)
}
}
mod inspect {
use super::*;
use std::fmt::Write;
#[test]
fn some_5() {
let some_5 = Some(5);
let mut text = String::new();
some_5.inspect(|x| write!(text, "{x}").expect("failed to write {x} to text"));
assert_eq!(text, "5");
Transform::inspect(some_5, |x| {
write!(text, "{x}").expect("failed to write {x} to text")
});
assert_eq!(text, "55");
}
}
mod map {
use super::*;
#[test]
fn map() {
let some_5 = Some(5);
let stdlib = some_5.map(|x| x + 1);
let custom = Transform::map(some_5, |x| x + 1);
assert_eq!(stdlib, custom);
}
#[test]
fn map_or_some() {
let some_5 = Some(5);
let stdlib = some_5.map_or(0, |x| x + 1);
let custom = Transform::map_or(some_5, 0, |x| x + 1);
assert_eq!(stdlib, custom);
}
#[test]
fn map_or_none() {
let some_5: Option<u32> = None;
let stdlib = some_5.map_or(0, |x| x + 1);
let custom = Transform::map_or(some_5, 0, |x| x + 1);
assert_eq!(stdlib, custom);
}
#[test]
fn map_or_else_some() {
let some_5 = Some(5);
let stdlib = some_5.map_or_else(|| 1 + 1, |x| x + 1);
let custom = Transform::map_or_else(some_5, || 1 + 1, |x| x + 1);
assert_eq!(stdlib, custom);
}
#[test]
fn map_or_else_none() {
let some_5: Option<u32> = None;
let stdlib = some_5.map_or_else(|| 1 + 1, |x| x + 1);
let custom = Transform::map_or_else(some_5, || 1 + 1, |x| x + 1);
assert_eq!(stdlib, custom);
}
}
mod transpose {
use super::*;
#[test]
fn ok_some() {
let ok_some: Result<Option<u32>, String> = Ok(Some(5));
let stdlib: Option<Result<u32, String>> = ok_some.clone().transpose();
let custom: Option<Result<u32, String>> = Transform::transpose(ok_some);
assert_eq!(stdlib, custom)
}
#[test]
fn ok_none() {
let ok_none: Result<Option<u32>, String> = Ok(None);
let stdlib = ok_none.clone().transpose();
let custom = Transform::transpose(ok_none);
assert_eq!(stdlib, custom)
}
#[test]
fn err() {
let err: Result<Option<i32>, String> = Err("Oops".to_string());
let stdlib = err.clone().transpose();
let custom = Transform::transpose(err);
assert_eq!(stdlib, custom)
}
#[test]
fn some_ok() {
let some_ok: Option<Result<u32, String>> = Some(Ok(5));
let stdlib: Result<Option<u32>, String> = some_ok.clone().transpose();
let custom: Result<Option<u32>, String> = Transform::transpose(some_ok);
assert_eq!(stdlib, custom)
}
#[test]
fn some_err() {
let some_err: Option<Result<u32, String>> = Some(Err("Oops".to_string()));
let stdlib = some_err.clone().transpose();
let custom = Transform::transpose(some_err);
assert_eq!(stdlib, custom)
}
#[test]
fn none() {
let none: Option<Result<u32, String>> = None;
let stdlib = none.clone().transpose();
let custom = Transform::transpose(none);
assert_eq!(stdlib, custom)
}
}
mod zip {
use super::*;
#[test]
fn some_some() {
let some_1 = Some(1);
let some_x = Some("x");
let stdlib = some_1.zip(some_x);
let custom = Transform::zip(some_1, some_x);
assert_eq!(stdlib, custom);
}
#[test]
fn some_some_with() {
let some_1 = Some(-1_i32);
let some_2 = Some(2_u16);
let stdlib = some_1.zip_with(some_2, |x, y| x + i32::from(y));
let custom = Transform::zip_with(some_1, some_2, |x, y| x + i32::from(y));
assert_eq!(stdlib, custom);
}
#[test]
fn reverse_infer() {
let some_1 = Some(-1_i32);
let some_2 = Some(2_u16);
let custom: Option<()> = Transform::zip_with(some_1, some_2, |_, _| Default::default());
assert_eq!(custom, Some(()));
}
}
}