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 {}