use std::{
borrow::{Borrow, BorrowMut},
ops::{Deref, DerefMut},
};
pub trait Pipe {
#[inline]
fn pipe<R, F>(self, f: F) -> R
where
Self: Sized,
F: FnOnce(Self) -> R,
{
f(self)
}
#[inline]
fn pipe_ref<'a, R, F>(&'a self, f: F) -> R
where
F: FnOnce(&'a Self) -> R,
{
f(self)
}
#[inline]
fn pipe_mut<'a, R, F>(&'a mut self, f: F) -> R
where
F: FnOnce(&'a mut Self) -> R,
{
f(self)
}
#[inline]
fn pipe_as_ref<'a, P, R, F>(&'a self, f: F) -> R
where
Self: AsRef<P>,
P: ?Sized + 'a,
F: FnOnce(&'a P) -> R,
{
f(self.as_ref())
}
#[inline]
fn pipe_as_mut<'a, P, R, F>(&'a mut self, f: F) -> R
where
Self: AsMut<P>,
P: ?Sized + 'a,
F: FnOnce(&'a mut P) -> R,
{
f(self.as_mut())
}
#[inline]
fn pipe_deref<'a, Param, R, F>(&'a self, f: F) -> R
where
Self: Deref<Target = Param>,
Param: ?Sized + 'a,
F: FnOnce(&'a Param) -> R,
{
f(self)
}
#[inline]
fn pipe_deref_mut<'a, Param, R, F>(&'a mut self, f: F) -> R
where
Self: DerefMut<Target = Param>,
Param: ?Sized + 'a,
F: FnOnce(&'a mut Param) -> R,
{
f(self)
}
#[inline]
fn pipe_borrow<'a, Param, R, F>(&'a self, f: F) -> R
where
Self: Borrow<Param>,
Param: ?Sized + 'a,
F: FnOnce(&'a Param) -> R,
{
f(self.borrow())
}
#[inline]
fn pipe_borrow_mut<'a, Param, R, F>(&'a mut self, f: F) -> R
where
Self: BorrowMut<Param>,
Param: ?Sized + 'a,
F: FnOnce(&'a mut Param) -> R,
{
f(self.borrow_mut())
}
}
impl<T> Pipe for T {}
#[cfg(test)]
mod tests {
use futures::executor::block_on;
use futures::future::lazy;
use super::Pipe;
async fn async_fn(s: String) -> String {
lazy(|_| format!("a({})", s)).await
}
fn result_fn(s: String) -> Result<String, ()> {
Ok(format!("r({})", s))
}
#[test]
fn simple() {
assert_eq!("foo".pipe(Some), Some("foo"));
}
#[test]
fn chaining() {
let a: Result<String, ()> = block_on(async {
"foo"
.to_string()
.pipe(result_fn)?
.pipe(|x| format!("c({})", x))
.pipe(async_fn)
.await
.replace("f", "b")
.pipe(Ok)
});
let b: Result<String, ()> = block_on(async {
Ok(async_fn(format!("c({})", result_fn("foo".to_string())?))
.await
.replace("f", "b"))
});
assert_eq!(a, b);
assert_eq!(a, Ok(String::from("a(c(r(boo)))")));
}
#[test]
fn same_type() {
let x: i32 = 3;
let inc = |x| x + 1;
let double = |x| x + x;
let square = |x| x * x;
let a = (x).pipe(inc).pipe(double).pipe(square);
let b = square(double(inc(x)));
assert_eq!(a, b);
}
#[test]
fn type_transformation() {
let x = 'x';
let a = x.pipe(|x| (x, x, x)).pipe(|x| [x, x]);
let b = [('x', 'x', 'x'), ('x', 'x', 'x')];
assert_eq!(a, b);
}
#[test]
fn slice() {
let vec: &[i32] = &[0, 1, 2, 3];
let vec = vec.pipe(|x: &[i32]| [x, &[4, 5, 6]].concat());
assert_eq!(vec, [0, 1, 2, 3, 4, 5, 6]);
}
#[test]
fn trait_object() {
use core::{cmp::PartialEq, fmt::Display, marker::Copy};
fn run(x: impl AsRef<str> + PartialEq + Display + Copy + ?Sized) {
let x = x.pipe(|x| x);
assert_eq!(x.as_ref(), "abc");
}
run("abc");
}
#[test]
#[allow(clippy::blacklisted_name)]
fn pipe_ref() {
#[derive(Debug, PartialEq, Eq)]
struct FooBar(i32);
let foo = FooBar(12);
let bar = foo.pipe_ref(|x| x.0).pipe(FooBar);
assert_eq!(foo, bar);
}
#[test]
#[allow(clippy::blacklisted_name)]
fn pipe_ref_lifetime_bound() {
#[derive(Debug, PartialEq, Eq)]
struct Foo;
fn f(foo: &'_ Foo) -> &'_ Foo {
foo
}
Foo.pipe_ref(f).pipe_ref(f);
}
#[test]
#[allow(clippy::blacklisted_name)]
fn pipe_mut() {
#[derive(Debug, PartialEq, Eq)]
struct Foo(i32);
let mut foo = Foo(0);
foo.pipe_mut(|x| x.0 = 32);
assert_eq!(foo, Foo(32));
}
#[test]
#[allow(clippy::blacklisted_name)]
fn pipe_mut_lifetime_bound() {
#[derive(Debug, PartialEq, Eq)]
struct Foo(i32, i32, i32);
impl Foo {
pub fn new() -> Self {
Self(0, 0, 0)
}
pub fn set_0(&mut self, x: i32) -> &mut Self {
self.0 = x;
self
}
pub fn set_1(&mut self, x: i32) -> &mut Self {
self.1 = x;
self
}
pub fn set_2(&mut self, x: i32) -> &mut Self {
self.2 = x;
self
}
}
let mut expected = Foo::new();
let expected = expected.set_0(123).set_1(456).set_2(789);
fn modify(foo: &mut Foo) -> &mut Foo {
foo.set_0(123).set_1(456).set_2(789);
foo
}
let mut actual = Foo::new();
let actual = actual.pipe_mut(modify);
assert_eq!(actual, expected);
}
}