tap/
lib.rs

1/*! # `tap` – Syntactical Plumb-Lines
2
3Rust permits functions that take a `self` receiver to be written in “dot-call”
4suffix position, rather than the more traditional prefix-position function call
5syntax. These functions are restricted to `impl [Trait for] Type` blocks, and
6functions anywhere else cannot take advantage of this syntax.
7
8This crate provides universally-implemented extension traits that permit smooth
9suffix-position calls for a handful of common operations: transparent inspection
10or modification (tapping), transformation (piping), and type conversion.
11
12## Tapping
13
14The [`tap`] module provides the [`Tap`], [`TapOptional`], and [`TapFallible`]
15traits. Each of these traits provides methods that take and return a value, and
16expose it as a borrow to an effect function. They look like this:
17
18```rust
19use tap::prelude::*;
20# struct Tmp;
21# fn make_value() -> Tmp { Tmp }
22# impl Tmp { fn process_value(self) {} }
23# macro_rules! log { ($msg:literal, $val:ident) => {{}}; }
24
25let end = make_value()
26  .tap(|v| log!("Produced value: {:?}", v))
27  .process_value();
28```
29
30These methods are `self -> Self`, and return the value they received without
31any transformation. This enables them to be placed anywhere in a larger
32expression witohut changing its shape, or causing any semantic changes to the
33code. The effect function receives a borrow of the tapped value, optionally run
34through the `Borrow`, `AsRef`, or `Deref` view conversions, for the duration of
35its execution.
36
37The effect function cannot return a value, as the tap is incapable of handling
38it.
39
40## Piping
41
42The [`pipe`] module provides the [`Pipe`] trait. This trait provides methods
43that take and transform a value, returning the result of the transformation.
44They look like this:
45
46```rust
47use tap::prelude::*;
48
49struct One;
50fn start() -> One { One }
51struct Two;
52fn end(_: One) -> Two { Two }
53
54let val: Two = start().pipe(end);
55
56// without pipes, this would be written as
57let _: Two = end(start());
58```
59
60These methods are `self -> Other`, and return the value produced by the effect
61function. As the methods are always available in suffix position, they can take
62as arguments methods that are *not* eligible for dot-call syntax and still place
63them as expression suffices. The effect function receives the piped value,
64optionally run through the `Borrow`, `AsRef`, or `Deref` view conversions, as
65its input, and its output is returned from the pipe.
66
67For `.pipe()`, the input value is *moved* into the pipe and the effect function,
68so the effect function *cannot* return a value whose lifetime depends on the
69input value. The other pipe methods all borrow the input value, and may return a
70value whose lifetime is tied to it.
71
72## Converting
73
74The [`conv`] module provides the [`Conv`] and [`TryConv`] traits. These provide
75methods that accept a type parameter on the method name, and forward to the
76appropriate `Into` or `TryInto` trait implementation when called. The difference
77between `Conv` and `Into` is that `Conv` is declared as `Conv::conv::<T>`, while
78`Into` is declared as `Into::<T>::into`. The location of the destination type
79parameter makes `.into()` unusable as a non-terminal method call of an
80expression, while `.conv::<T>()` can be used as a method call anywhere.
81
82```rust,compile_fail
83let upper = "hello, world"
84  .into()
85  .tap_mut(|s| s.make_ascii_uppercase());
86```
87
88The above snippet is illegal, because the Rust type solver cannot determine the
89type of the sub-expression `"hello, world".into()`, and it will not attempt to
90search all available `impl Into<X> for str` implementations to find an `X` which
91has a
92`fn tap_mut({self, &self, &mut self, Box<Self>, Rc<Self>, Arc<Self>}, _) -> Y`
93declared, either as an inherent method or in a trait implemented by `X`, to
94resolve the expression.
95
96Instead, you can write it as
97
98```rust
99use tap::prelude::*;
100
101let upper = "hello, world"
102  .conv::<String>()
103  .tap_mut(|s| s.make_ascii_uppercase());
104```
105
106The trait implementation is
107
108```rust
109pub trait Conv: Sized {
110 fn conv<T: Sized>(self) -> T
111 where Self: Into<T> {
112  self.into()
113 }
114}
115```
116
117Each monomorphization of `.conv::<T>()` expands to the appropriate `Into<T>`
118implementation, and does nothing else.
119
120[`Conv`]: conv/trait.Conv.html
121[`Pipe`]: pipe/trait.Pipe.html
122[`Tap`]: tap/trait.Tap.html
123[`TapFallible`]: tap/trait.TapFallible.html
124[`TapOptional`]: tap/trait.TapOptional.html
125[`TryConv`]: conv/trait.TryConv.html
126[`conv`]: conv/index.html
127[`pipe`]: pipe/index.html
128[`tap`]: tap/index.html
129!*/
130
131#![no_std]
132#![cfg_attr(debug_assertions, warn(missing_docs))]
133#![cfg_attr(not(debug_assertions), deny(missing_docs))]
134
135pub mod conv;
136pub mod pipe;
137pub mod tap;
138
139/// Reëxports all traits in one place, for easy import.
140pub mod prelude {
141	#[doc(inline)]
142	pub use crate::{conv::*, pipe::*, tap::*};
143}
144
145// also make traits available at crate root
146#[doc(inline)]
147pub use prelude::*;