tap/tap.rs
1/*! # Point-Free Inspection
2
3The standard library does not provide a way to view or modify an expression
4without binding it to a name. This module provides extension methods that take
5and return a value, allowing it to be temporarily bound without creating a new
6`let`-statement in the enclosing scope.
7
8The two main uses of these methods are to temporarily attach debugging
9tracepoints to an expression without modifying its surrounding code, or to
10temporarily mutate an otherwise-immutable object.
11
12For convenience, methods are available that will modify the *view* of the tapped
13object that is passed to the effect function, by using the value’s
14`Borrow`/`BorrowMut`, `AsRef`/`AsMut`, or `Index`/`IndexMut` trait
15implementations. For example, the `Vec` collection has no `fn sort` method: this
16is actually implemented on slices, to which `Vec` dereferences.
17
18```rust
19use tap::tap::*;
20# fn make_vec() -> Vec<i32> { vec![] }
21
22// taps take ordinary closures, which can use deref coercion
23make_vec().tap_mut(|v| v.sort());
24// `Vec<T>` implements `BorrowMut<[T]>`,
25make_vec().tap_borrow_mut(<[_]>::sort);
26// and `AsMut<[T]>`,
27make_vec().tap_ref_mut(<[_]>::sort);
28// and `DerefMut<Target = [T]>,
29make_vec().tap_deref_mut(<[_]>::sort);
30// but has no inherent method `sort`.
31// make_vec().tap_mut(Vec::sort);
32```
33!*/
34
35use core::{
36 borrow::{Borrow, BorrowMut},
37 ops::{Deref, DerefMut},
38};
39
40/** Point-free value inspection and modification.
41
42This trait provides methods that permit viewing the value of an expression
43without requiring a new `let` binding or any other alterations to the original
44code other than insertion of the `.tap()` call.
45
46The methods in this trait do not perform any view conversions on the value they
47receive; it is borrowed and passed directly to the effect argument.
48**/
49pub trait Tap
50where
51 Self: Sized,
52{
53 /// Immutable access to a value.
54 ///
55 /// This function permits a value to be viewed by some inspecting function
56 /// without affecting the overall shape of the expression that contains this
57 /// method call. It is useful for attaching assertions or logging points
58 /// into a multi-part expression.
59 ///
60 /// # Examples
61 ///
62 /// Here we use `.tap()` to attach logging tracepoints to each stage of a
63 /// value-processing pipeline.
64 ///
65 /// ```rust
66 /// use tap::tap::Tap;
67 /// # struct Tmp;
68 /// # impl Tmp { fn process_value(self) -> Self { self } }
69 /// # fn make_value() -> Tmp { Tmp }
70 /// # macro_rules! log { ($msg:literal, $x:ident) => {{}}; }
71 ///
72 /// let end = make_value()
73 /// // this line has no effect on the rest of the code
74 /// .tap(|v| log!("The produced value was: {}", v))
75 /// .process_value();
76 /// ```
77 #[inline(always)]
78 fn tap(self, func: impl FnOnce(&Self)) -> Self {
79 func(&self);
80 self
81 }
82
83 /// Mutable access to a value.
84 ///
85 /// This function permits a value to be modified by some function without
86 /// affecting the overall shape of the expression that contains this method
87 /// call. It is useful for attaching modifier functions that have an
88 /// `&mut Self -> ()` signature to an expression, without requiring an
89 /// explicit `let mut` binding.
90 ///
91 /// # Examples
92 ///
93 /// Here we use `.tap_mut()` to sort an array without requring multiple
94 /// bindings.
95 ///
96 /// ```rust
97 /// use tap::tap::Tap;
98 ///
99 /// let sorted = [1i32, 5, 2, 4, 3]
100 /// .tap_mut(|arr| arr.sort());
101 /// assert_eq!(sorted, [1, 2, 3, 4, 5]);
102 /// ```
103 ///
104 /// Without tapping, this would be written as
105 ///
106 /// ```rust
107 /// let mut received = [1, 5, 2, 4, 3];
108 /// received.sort();
109 /// let sorted = received;
110 /// ```
111 ///
112 /// The mutable tap is a convenient alternative when the expression to
113 /// produce the collection is more complex, for example, an iterator
114 /// pipeline collected into a vector.
115 #[inline(always)]
116 fn tap_mut(mut self, func: impl FnOnce(&mut Self)) -> Self {
117 func(&mut self);
118 self
119 }
120
121 /// Immutable access to the `Borrow<B>` of a value.
122 ///
123 /// This function is identcal to [`Tap::tap`], except that the effect
124 /// function recevies an `&B` produced by `Borrow::<B>::borrow`, rather than
125 /// an `&Self`.
126 ///
127 /// [`Tap::tap`]: trait.Tap.html#method.tap
128 #[inline(always)]
129 fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
130 where
131 Self: Borrow<B>,
132 B: ?Sized,
133 {
134 func(Borrow::<B>::borrow(&self));
135 self
136 }
137
138 /// Mutable access to the `BorrowMut<B>` of a value.
139 ///
140 /// This function is identical to [`Tap::tap_mut`], except that the effect
141 /// function receives an `&mut B` produced by `BorrowMut::<B>::borrow_mut`,
142 /// rather than an `&mut Self`.
143 ///
144 /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
145 #[inline(always)]
146 fn tap_borrow_mut<B>(mut self, func: impl FnOnce(&mut B)) -> Self
147 where
148 Self: BorrowMut<B>,
149 B: ?Sized,
150 {
151 func(BorrowMut::<B>::borrow_mut(&mut self));
152 self
153 }
154
155 /// Immutable access to the `AsRef<R>` view of a value.
156 ///
157 /// This function is identical to [`Tap::tap`], except that the effect
158 /// function receives an `&R` produced by `AsRef::<R>::as_ref`, rather than
159 /// an `&Self`.
160 ///
161 /// [`Tap::tap`]: trait.Tap.html#method.tap
162 #[inline(always)]
163 fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
164 where
165 Self: AsRef<R>,
166 R: ?Sized,
167 {
168 func(AsRef::<R>::as_ref(&self));
169 self
170 }
171
172 /// Mutable access to the `AsMut<R>` view of a value.
173 ///
174 /// This function is identical to [`Tap::tap_mut`], except that the effect
175 /// function receives an `&mut R` produced by `AsMut::<R>::as_mut`, rather
176 /// than an `&mut Self`.
177 ///
178 /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
179 #[inline(always)]
180 fn tap_ref_mut<R>(mut self, func: impl FnOnce(&mut R)) -> Self
181 where
182 Self: AsMut<R>,
183 R: ?Sized,
184 {
185 func(AsMut::<R>::as_mut(&mut self));
186 self
187 }
188
189 /// Immutable access to the `Deref::Target` of a value.
190 ///
191 /// This function is identical to [`Tap::tap`], except that the effect
192 /// function receives an `&Self::Target` produced by `Deref::deref`, rather
193 /// than an `&Self`.
194 ///
195 /// [`Tap::tap`]: trait.Tap.html#method.tap
196 #[inline(always)]
197 fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
198 where
199 Self: Deref<Target = T>,
200 T: ?Sized,
201 {
202 func(Deref::deref(&self));
203 self
204 }
205
206 /// Mutable access to the `Deref::Target` of a value.
207 ///
208 /// This function is identical to [`Tap::tap_mut`], except that the effect
209 /// function receives an `&mut Self::Target` produced by
210 /// `DerefMut::deref_mut`, rather than an `&mut Self`.
211 ///
212 /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
213 #[inline(always)]
214 fn tap_deref_mut<T>(mut self, func: impl FnOnce(&mut T)) -> Self
215 where
216 Self: DerefMut + Deref<Target = T>,
217 T: ?Sized,
218 {
219 func(DerefMut::deref_mut(&mut self));
220 self
221 }
222
223 // debug-build-only copies of the above methods
224
225 /// Calls `.tap()` only in debug builds, and is erased in release builds.
226 #[inline(always)]
227 fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self {
228 if cfg!(debug_assertions) {
229 func(&self);
230 }
231 self
232 }
233
234 /// Calls `.tap_mut()` only in debug builds, and is erased in release
235 /// builds.
236 #[inline(always)]
237 fn tap_mut_dbg(mut self, func: impl FnOnce(&mut Self)) -> Self {
238 if cfg!(debug_assertions) {
239 func(&mut self);
240 }
241 self
242 }
243
244 /// Calls `.tap_borrow()` only in debug builds, and is erased in release
245 /// builds.
246 #[inline(always)]
247 fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
248 where
249 Self: Borrow<B>,
250 B: ?Sized,
251 {
252 if cfg!(debug_assertions) {
253 func(Borrow::<B>::borrow(&self));
254 }
255 self
256 }
257
258 /// Calls `.tap_borrow_mut()` only in debug builds, and is erased in release
259 /// builds.
260 #[inline(always)]
261 fn tap_borrow_mut_dbg<B>(mut self, func: impl FnOnce(&mut B)) -> Self
262 where
263 Self: BorrowMut<B>,
264 B: ?Sized,
265 {
266 if cfg!(debug_assertions) {
267 func(BorrowMut::<B>::borrow_mut(&mut self));
268 }
269 self
270 }
271
272 /// Calls `.tap_ref()` only in debug builds, and is erased in release
273 /// builds.
274 #[inline(always)]
275 fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
276 where
277 Self: AsRef<R>,
278 R: ?Sized,
279 {
280 if cfg!(debug_assertions) {
281 func(AsRef::<R>::as_ref(&self));
282 }
283 self
284 }
285
286 /// Calls `.tap_ref_mut()` only in debug builds, and is erased in release
287 /// builds.
288 #[inline(always)]
289 fn tap_ref_mut_dbg<R>(mut self, func: impl FnOnce(&mut R)) -> Self
290 where
291 Self: AsMut<R>,
292 R: ?Sized,
293 {
294 if cfg!(debug_assertions) {
295 func(AsMut::<R>::as_mut(&mut self));
296 }
297 self
298 }
299
300 /// Calls `.tap_deref()` only in debug builds, and is erased in release
301 /// builds.
302 #[inline(always)]
303 fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
304 where
305 Self: Deref<Target = T>,
306 T: ?Sized,
307 {
308 if cfg!(debug_assertions) {
309 func(Deref::deref(&self));
310 }
311 self
312 }
313
314 /// Calls `.tap_deref_mut()` only in debug builds, and is erased in release
315 /// builds.
316 #[inline(always)]
317 fn tap_deref_mut_dbg<T>(mut self, func: impl FnOnce(&mut T)) -> Self
318 where
319 Self: DerefMut + Deref<Target = T>,
320 T: ?Sized,
321 {
322 if cfg!(debug_assertions) {
323 func(DerefMut::deref_mut(&mut self));
324 }
325 self
326 }
327}
328
329impl<T> Tap for T where T: Sized {}
330
331/** Optional tapping, conditional on the optional presence of a value.
332
333This trait is intended for use on types that express the concept of “optional
334presence”, primarily the [`Option`] monad. It provides taps that inspect the
335container to determine if the effect function should execute or not.
336
337> Note: This trait is a specialization of [`TapFallible`], and exists because
338> the [`std::ops::Try`] trait is still unstable. When `Try` stabilizes, this
339> trait can be removed, and `TapFallible` blanket-applied to all `Try`
340> implementors.
341
342[`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
343[`TapFallible`]: trait.TapFallible.html
344[`std::ops::Try`]: https://doc.rust-lang.org/std/ops/trait.Try.html
345**/
346pub trait TapOptional
347where
348 Self: Sized,
349{
350 /// The interior type that the container may or may not carry.
351 type Val: ?Sized;
352
353 /// Immutabily accesses an interior value only when it is present.
354 ///
355 /// This function is identical to [`Tap::tap`], except that it is required
356 /// to check the implementing container for value presence before running.
357 /// Implementors must not run the effect function if the container is marked
358 /// as being empty.
359 ///
360 /// [`Tap::tap`]: trait.Tap.html#method.tap
361 fn tap_some(self, func: impl FnOnce(&Self::Val)) -> Self;
362
363 /// Mutably accesses an interor value only when it is present.
364 ///
365 /// This function is identical to [`Tap::tap_mut`], except that it is
366 /// required to check the implementing container for value presence before
367 /// running. Implementors must not run the effect function if the container
368 /// is marked as being empty.
369 ///
370 /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
371 fn tap_some_mut(self, func: impl FnOnce(&mut Self::Val)) -> Self;
372
373 /// Runs an effect function when the container is empty.
374 ///
375 /// This function is identical to [`Tap::tap`], except that it is required
376 /// to check the implementing container for value absence before running.
377 /// Implementors must not run the effect function if the container is marked
378 /// as being non-empty.
379 ///
380 /// [`Tap::tap`]: trait.Tap.html#method.tap
381 fn tap_none(self, func: impl FnOnce()) -> Self;
382
383 /// Calls `.tap_some()` only in debug builds, and is erased in release
384 /// builds.
385 #[inline(always)]
386 fn tap_some_dbg(self, func: impl FnOnce(&Self::Val)) -> Self {
387 if cfg!(debug_assertions) {
388 self.tap_some(func)
389 } else {
390 self
391 }
392 }
393
394 /// Calls `.tap_some_mut()` only in debug builds, and is erased in release
395 /// builds.
396 #[inline(always)]
397 fn tap_some_mut_dbg(self, func: impl FnOnce(&mut Self::Val)) -> Self {
398 if cfg!(debug_assertions) {
399 self.tap_some_mut(func)
400 } else {
401 self
402 }
403 }
404
405 /// Calls `.tap_none()` only in debug builds, and is erased in release
406 /// builds.
407 #[inline(always)]
408 fn tap_none_dbg(self, func: impl FnOnce()) -> Self {
409 if cfg!(debug_assertions) {
410 self.tap_none(func)
411 } else {
412 self
413 }
414 }
415}
416
417impl<T> TapOptional for Option<T> {
418 type Val = T;
419
420 #[inline(always)]
421 fn tap_some(self, func: impl FnOnce(&T)) -> Self {
422 if let Some(ref val) = self {
423 func(val);
424 }
425 self
426 }
427
428 #[inline(always)]
429 fn tap_some_mut(mut self, func: impl FnOnce(&mut T)) -> Self {
430 if let Some(ref mut val) = self {
431 func(val);
432 }
433 self
434 }
435
436 #[inline(always)]
437 fn tap_none(self, func: impl FnOnce()) -> Self {
438 if self.is_none() {
439 func();
440 }
441 self
442 }
443}
444
445/** Fallible tapping, conditional on the optional success of an expression.
446
447This trait is intended for use on types that express the concept of “fallible
448presence”, primarily the [`Result`] monad. It provides taps that inspect the
449container to determine if the effect function should execute or not.
450
451> Note: This trait would ideally be implemented as a blanket over all
452> [`std::ops::Try`] implementors. When `Try` stabilizes, this crate can be
453> updated to do so.
454
455[`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
456[`std::ops::Try`]: https://doc.rust-lang.org/std/ops/trait.Try.html
457**/
458pub trait TapFallible
459where
460 Self: Sized,
461{
462 /// The interior type used to indicate a successful construction.
463 type Ok: ?Sized;
464
465 /// The interior type used to indicate a failed construction.
466 type Err: ?Sized;
467
468 /// Immutably accesses an interior success value.
469 ///
470 /// This function is identical to [`Tap::tap`], except that it is required
471 /// to check the implementing container for value success before running.
472 /// Implementors must not run the effect function if the container is marked
473 /// as being a failure.
474 ///
475 /// [`Tap::tap`]: trait.Tap.html#method.tap
476 fn tap_ok(self, func: impl FnOnce(&Self::Ok)) -> Self;
477
478 /// Mutably accesses an interior success value.
479 ///
480 /// This function is identical to [`Tap::tap_mut`], except that it is
481 /// required to check the implementing container for value success before
482 /// running. Implementors must not run the effect function if the container
483 /// is marked as being a failure.
484 ///
485 /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
486 fn tap_ok_mut(self, func: impl FnOnce(&mut Self::Ok)) -> Self;
487
488 /// Immutably accesses an interior failure value.
489 ///
490 /// This function is identical to [`Tap::tap`], except that it is required
491 /// to check the implementing container for value failure before running.
492 /// Implementors must not run the effect function if the container is marked
493 /// as being a success.
494 ///
495 /// [`Tap::tap`]: trait.Tap.html#method.tap
496 fn tap_err(self, func: impl FnOnce(&Self::Err)) -> Self;
497
498 /// Mutably accesses an interior failure value.
499 ///
500 /// This function is identical to [`Tap::tap_mut`], except that it is
501 /// required to check the implementing container for value failure before
502 /// running. Implementors must not run the effect function if the container
503 /// is marked as being a success.
504 ///
505 /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
506 fn tap_err_mut(self, func: impl FnOnce(&mut Self::Err)) -> Self;
507
508 /// Calls `.tap_ok()` only in debug builds, and is erased in release builds.
509 #[inline(always)]
510 fn tap_ok_dbg(self, func: impl FnOnce(&Self::Ok)) -> Self {
511 if cfg!(debug_assertions) {
512 self.tap_ok(func)
513 } else {
514 self
515 }
516 }
517
518 /// Calls `.tap_ok_mut()` only in debug builds, and is erased in release
519 /// builds.
520 #[inline(always)]
521 fn tap_ok_mut_dbg(self, func: impl FnOnce(&mut Self::Ok)) -> Self {
522 if cfg!(debug_assertions) {
523 self.tap_ok_mut(func)
524 } else {
525 self
526 }
527 }
528
529 /// Calls `.tap_err()` only in debug builds, and is erased in release
530 /// builds.
531 #[inline(always)]
532 fn tap_err_dbg(self, func: impl FnOnce(&Self::Err)) -> Self {
533 if cfg!(debug_assertions) {
534 self.tap_err(func)
535 } else {
536 self
537 }
538 }
539
540 /// Calls `.tap_err_mut()` only in debug builds, and is erased in release
541 /// builds.
542 #[inline(always)]
543 fn tap_err_mut_dbg(self, func: impl FnOnce(&mut Self::Err)) -> Self {
544 if cfg!(debug_assertions) {
545 self.tap_err_mut(func)
546 } else {
547 self
548 }
549 }
550}
551
552impl<T, E> TapFallible for Result<T, E> {
553 type Ok = T;
554 type Err = E;
555
556 #[inline(always)]
557 fn tap_ok(self, func: impl FnOnce(&T)) -> Self {
558 if let Ok(ref val) = self {
559 func(val);
560 }
561 self
562 }
563
564 #[inline(always)]
565 fn tap_ok_mut(mut self, func: impl FnOnce(&mut T)) -> Self {
566 if let Ok(ref mut val) = self {
567 func(val);
568 }
569 self
570 }
571
572 #[inline(always)]
573 fn tap_err(self, func: impl FnOnce(&E)) -> Self {
574 if let Err(ref val) = self {
575 func(val);
576 }
577 self
578 }
579
580 #[inline(always)]
581 fn tap_err_mut(mut self, func: impl FnOnce(&mut E)) -> Self {
582 if let Err(ref mut val) = self {
583 func(val);
584 }
585 self
586 }
587}