tap/
pipe.rs

1/*! # Universal Suffix Calls
2
3This module provides a single trait, `Pipe`, which provides a number of methods
4useful for placing functions in suffix position. The most common method, `pipe`,
5forwards a value `T` into any function `T -> R`, returning `R`. The other
6methods all apply some form of borrowing to the value before passing the borrow
7into the piped function. These are of less value, but provided to maintain a
8similar API to the `tap` module’s methods, and for convenience in the event that
9you do have a use for them.
10
11This module is as much of a [UFCS] method syntax that can be provided as a
12library, rather than in the language grammar.
13
14[UFCS]: https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax
15!*/
16
17use core::{
18	borrow::{Borrow, BorrowMut},
19	ops::{Deref, DerefMut},
20};
21
22/** Provides universal suffix-position call syntax for any function.
23
24This trait provides methods that allow any closure or free function to be placed
25as a suffix-position call, by writing them as
26
27```rust
28# use tap::pipe::Pipe;
29# let receiver = 5;
30fn not_a_method(x: i32) -> u8 { x as u8 }
31receiver.pipe(not_a_method);
32```
33
34Piping into functions that take more than one argument still requires writing a
35closure with ordinary function-call syntax. This is after all only a library,
36not a syntax transformation:
37
38```rust
39use tap::pipe::Pipe;
40fn add(x: i32, y: i32) -> i32 { x + y }
41
42let out = 5.pipe(|x| add(x, 10));
43assert_eq!(out, 15);
44```
45
46Like tapping, piping is useful for cases where you want to write a sequence of
47processing steps without introducing many intermediate bindings, and your steps
48contain functions which are not eligible for dot-call syntax.
49
50The main difference between piping and tapping is that tapping always returns
51the value that was passed into the tap, while piping forwards the value into the
52effect function, and returns the output of evaluating the effect function with
53the value. Piping is a transformation, not merely an inspection or modification.
54**/
55pub trait Pipe {
56	/// Pipes by value. This is generally the method you want to use.
57	///
58	/// # Examples
59	///
60	/// ```rust
61	/// use tap::pipe::Pipe;
62	///
63	/// fn triple(x: i32) -> i64 {
64	///   x as i64 * 3
65	/// }
66	///
67	/// assert_eq!(
68	///   10.pipe(triple),
69	///   30,
70	/// );
71	/// ```
72	#[inline(always)]
73	fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
74	where
75		Self: Sized,
76		R: Sized,
77	{
78		func(self)
79	}
80
81	/// Borrows `self` and passes that borrow into the pipe function.
82	///
83	/// # Examples
84	///
85	/// ```rust
86	/// use tap::pipe::Pipe;
87	///
88	/// fn fold(v: &Vec<i32>) -> i32 {
89	///   v.iter().copied().sum()
90	/// }
91	/// let vec = vec![1, 2, 3, 4, 5];
92	/// let sum = vec.pipe_ref(fold);
93	/// assert_eq!(sum, 15);
94	/// assert_eq!(vec.len(), 5);
95	/// ```
96	#[inline(always)]
97	fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
98	where
99		R: 'a + Sized,
100	{
101		func(self)
102	}
103
104	/// Mutably borrows `self` and passes that borrow into the pipe function.
105	///
106	/// # Examples
107	///
108	/// ```rust
109	/// use tap::pipe::Pipe;
110	///
111	/// let mut vec = vec![false, true];
112	/// let last = vec
113	///   .pipe_ref_mut(Vec::pop)
114	///   .pipe(Option::unwrap);
115	/// assert!(last);
116	/// ```
117	///
118	/// Both of these functions are eligible for method-call syntax, and should
119	/// not be piped. Writing out non-trivial examples for these is a lot of
120	/// boilerplate.
121	#[inline(always)]
122	fn pipe_ref_mut<'a, R>(
123		&'a mut self,
124		func: impl FnOnce(&'a mut Self) -> R,
125	) -> R
126	where
127		R: 'a + Sized,
128	{
129		func(self)
130	}
131
132	/// Borrows `self`, then passes `self.borrow()` into the pipe function.
133	///
134	/// # Examples
135	///
136	/// ```rust
137	/// use std::borrow::Cow;
138	/// use tap::pipe::Pipe;
139	///
140	/// let len = Cow::<'static, str>::from("hello, world")
141	///   .pipe_borrow(str::len);
142	/// assert_eq!(len, 12);
143	/// ```
144	#[inline(always)]
145	fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
146	where
147		Self: Borrow<B>,
148		B: 'a + ?Sized,
149		R: 'a + Sized,
150	{
151		func(Borrow::<B>::borrow(self))
152	}
153
154	/// Mutably borrows `self`, then passes `self.borrow_mut()` into the pipe
155	/// function.
156	///
157	/// ```rust
158	/// use tap::pipe::Pipe;
159	///
160	/// let mut txt = "hello, world".to_string();
161	/// let ptr = txt
162	///   .pipe_borrow_mut(str::as_mut_ptr);
163	/// ```
164	///
165	/// This is a very contrived example, but the `BorrowMut` trait has almost
166	/// no implementors in the standard library, and of the implementations
167	/// available, there are almost no methods that fit this API.
168	#[inline(always)]
169	fn pipe_borrow_mut<'a, B, R>(
170		&'a mut self,
171		func: impl FnOnce(&'a mut B) -> R,
172	) -> R
173	where
174		Self: BorrowMut<B>,
175		B: 'a + ?Sized,
176		R: 'a + Sized,
177	{
178		func(BorrowMut::<B>::borrow_mut(self))
179	}
180
181	/// Borrows `self`, then passes `self.as_ref()` into the pipe function.
182	#[inline(always)]
183	fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
184	where
185		Self: AsRef<U>,
186		U: 'a + ?Sized,
187		R: 'a + Sized,
188	{
189		func(AsRef::<U>::as_ref(self))
190	}
191
192	/// Mutably borrows `self`, then passes `self.as_mut()` into the pipe
193	/// function.
194	#[inline(always)]
195	fn pipe_as_mut<'a, U, R>(
196		&'a mut self,
197		func: impl FnOnce(&'a mut U) -> R,
198	) -> R
199	where
200		Self: AsMut<U>,
201		U: 'a + ?Sized,
202		R: 'a + Sized,
203	{
204		func(AsMut::<U>::as_mut(self))
205	}
206
207	/// Borrows `self`, then passes `self.deref()` into the pipe function.
208	#[inline(always)]
209	fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
210	where
211		Self: Deref<Target = T>,
212		T: 'a + ?Sized,
213		R: 'a + Sized,
214	{
215		func(Deref::deref(self))
216	}
217
218	/// Mutably borrows `self`, then passes `self.deref_mut()` into the pipe
219	/// function.
220	#[inline(always)]
221	fn pipe_deref_mut<'a, T, R>(
222		&'a mut self,
223		func: impl FnOnce(&'a mut T) -> R,
224	) -> R
225	where
226		Self: DerefMut + Deref<Target = T>,
227		T: 'a + ?Sized,
228		R: 'a + Sized,
229	{
230		func(DerefMut::deref_mut(self))
231	}
232}
233
234impl<T> Pipe for T where T: ?Sized {}