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}