Import tap 1.0.1 upstream upstream/1.0.1
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 22 Mar 2023 03:48:47 +0000 (12:48 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 22 Mar 2023 03:48:47 +0000 (12:48 +0900)
.cargo_vcs_info.json [new file with mode: 0644]
Cargo.toml [new file with mode: 0644]
Cargo.toml.orig [new file with mode: 0644]
LICENSE.txt [new file with mode: 0644]
README.md [new file with mode: 0644]
src/conv.rs [new file with mode: 0644]
src/lib.rs [new file with mode: 0644]
src/pipe.rs [new file with mode: 0644]
src/tap.rs [new file with mode: 0644]

diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644 (file)
index 0000000..8eb5390
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "f5315f0f5ca90ce6399daac76c1fe3ba645a4e4e"
+  }
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644 (file)
index 0000000..aa9750d
--- /dev/null
@@ -0,0 +1,25 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "tap"
+version = "1.0.1"
+authors = ["Elliott Linder <elliott.darfink@gmail.com>", "myrrlyn <self@myrrlyn.dev>"]
+include = ["Cargo.toml", "README.md", "LICENSE.txt", "src/**/*.rs"]
+description = "Generic extensions for tapping values in Rust"
+homepage = "https://github.com/myrrlyn/tap"
+documentation = "https://docs.rs/tap"
+readme = "README.md"
+keywords = ["tap", "pipe", "functional", "tap_ok", "tap_some"]
+categories = ["no-std", "rust-patterns"]
+license = "MIT"
+repository = "https://github.com/myrrlyn/tap"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644 (file)
index 0000000..cf42729
--- /dev/null
@@ -0,0 +1,30 @@
+[package]
+authors = [
+       "Elliott Linder <elliott.darfink@gmail.com>",
+       "myrrlyn <self@myrrlyn.dev>",
+]
+categories = [
+       "no-std",
+       "rust-patterns",
+]
+description = "Generic extensions for tapping values in Rust"
+documentation = "https://docs.rs/tap"
+homepage = "https://github.com/myrrlyn/tap"
+include = [
+       "Cargo.toml",
+       "README.md",
+       "LICENSE.txt",
+       "src/**/*.rs",
+]
+keywords = [
+       "tap",
+       "pipe",
+       "functional",
+       "tap_ok",
+       "tap_some",
+]
+license = "MIT"
+name = "tap"
+readme = "README.md"
+repository = "https://github.com/myrrlyn/tap"
+version = "1.0.1"
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644 (file)
index 0000000..6d9fc1e
--- /dev/null
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Elliot Linder <darfink@gmail.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644 (file)
index 0000000..3be34d6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,203 @@
+<div class="title-block" style="text-align: center;" align="center">
+
+# `tap` <!-- omit in toc -->
+
+## Suffix-Position Pipeline Behavior <!-- omit in toc -->
+
+[![Crate][crate_img]][crate]
+[![Documentation][docs_img]][docs]
+[![License][license_img]][license_file]
+
+[![Crate Downloads][downloads_img]][crate]
+[![Crate Size][loc_img]][loc]
+
+</div>
+
+This crate provides extension methods on all types that allow transparent,
+temporary, inspection/mutation (tapping), transformation (piping), or type
+conversion. These methods make it convenient for you to insert debugging or
+modification points into an expression without requiring you to change any other
+portions of your code.
+
+## Example Use
+
+### Tapping
+
+You can tap inside a method-chain expression for logging without requiring a
+rebind. For instance, you may write a complex expression without any
+intermediate debugging steps, and only later decide that you want them.
+Ordinarily, this transform would look like this:
+
+```rust
+extern crate reqwest;
+extern crate tracing;
+
+// old
+let body = reqwest::blocking::get("https://example.com")?
+  .text()?;
+tracing::debug!("Response contents: {}", body);
+
+// new, with debugging
+let resp = reqwest::blocking::get("https://example.com")?;
+tracing::debug!("Response status: {}", resp.status());
+let body = resp.text()?;
+tracing::debug!("Response contents: {}", body);
+```
+
+while with tapping, you can plug the logging statement directly into the overall
+expression, without making any other changes:
+
+```rust
+extern crate reqwest;
+extern crate tracing;
+
+let body = reqwest::blocking::get("https://example.com")?
+  // The only change is the insertion of this line
+  .tap(|resp| tracing::debug!("Response status: {}", resp.status()))
+  .text()?;
+tracing::debug!("Response contents: {}", body);
+```
+
+### Mutable Tapping
+
+Some APIs are written to require mutable borrows, rather than value-to-value
+transformations, which can require temporary rebinding in order to create
+mutability in an otherwise-immutable context. For example, collecting data into
+a vector, sorting the vector, and then freezing it, might look like this:
+
+```rust
+let mut collection = stream().collect::<Vec<_>>();
+collection.sort();
+// potential error site: inserting other mutations here
+let collection = collection; // now immutable
+```
+
+But with a mutable tap, you can avoid the duplicate binding *and* guard against
+future errors due to the presence of a mutable binding:
+
+```rust
+let collection = stream.collect::<Vec<_>>()
+  .tap_mut(|v| v.sort());
+```
+
+The `.tap_mut()` and related methods provide a mutable borrow to their argument,
+and allow the final binding site to choose their own level of mutability without
+exposing the intermediate permission.
+
+### Piping
+
+In addition to transparent inspection or modification points, you may also wish
+to use suffix calls for subsequent operations. For example, the standard library
+offers the free function `fs::read` to convert `Path`-like objects into
+`Vec<u8>` of their filesystem contents. Ordinarily, free functions require use
+as:
+
+```rust
+use std::fs;
+
+let mut path = get_base_path();
+path.push("logs");
+path.push(&format!("{}.log", today()));
+let contents = fs::read(path)?;
+```
+
+whereäs use of tapping (for path modification) and piping (for `fs::read`) could
+be expressed like this:
+
+```rust
+use std::fs;
+
+let contents = get_base_path()
+  .tap_mut(|p| p.push("logs"))
+  .tap_mut(|p| p.push(&format!("{}.log", today())))
+  .pipe(fs::read)?;
+```
+
+As a clearer example, consider the syntax required to apply multiple free
+funtions without `let`-bindings looks like this:
+
+```rust
+let val = last(
+  third(
+    second(
+      first(original_value),
+      another_arg,
+    )
+  ),
+  another_arg,
+);
+```
+
+which requires reading the expression in alternating, inside-out, order, to
+understand the full sequence of evaluation. With suffix calls, even free
+functions can be written in a point-free style that maintains a clear temporal
+and syntactic order:
+
+```rust
+let val = original_value
+  .pipe(first)
+  .pipe(|v| second(v, another_arg))
+  .pipe(third)
+  .pipe(|v| last(v, another_arg));
+```
+
+As piping is an ordinary method, not a syntax transformation, it still requires
+that you write function-call expressions when using a function with multiple
+arguments in the pipeline.
+
+### Conversion
+
+The `conv` module is the simplest: it provides two traits, `Conv` and `TryConv`,
+which are sibling traits to `Into<T>` and `TryInto<T>`. Their methods,
+`Conv::conv::<T>` and `TryConv::try_conv::<T>`, call the corresponding
+trait implementation, and allow you to use `.into()`/`.try_into()` in
+non-terminal method calls of an expression.
+
+```rust
+let bytes = "hello".into().into_bytes();
+```
+
+does not compile, because Rust cannot decide the type of `"hello".into()`.
+Instead of rewriting the expression to use an intermediate `let` binding, you
+can write it as
+
+```rust
+let bytes = "hello".conv::<String>().into_bytes();
+```
+
+## Full Functionality
+
+The `Tap` and `Pipe` traits both provide a large number of methods, which use
+different parts of the Rust language’s facilities for well-typed value access.
+Rather than repeat the API documentation here, you should view the module items
+in the [documentation][docs].
+
+As a summary, these traits provide methods that, upon receipt of a value,
+
+- apply no transformation
+- apply an `AsRef` or `AsMut` implementation
+- apply a `Borrow` or `BorrowMut` implementation
+- apply the `Deref` or `DerefMut` implementation
+
+before executing their effect argument.
+
+In addition, each `Tap` method `.tap_x` has a sibling method `.tap_x_dbg` that
+performs the same work, but only in debug builds; in release builds, the method
+call is stripped. This allows you to leave debugging taps in your source code,
+without affecting your project’s performance in true usage.
+
+Lastly, the `tap` module also has traits `TapOptional` and `TapFallible` which
+run taps on the variants of `Option` and `Result` enums, respectively, and do
+nothing when the variant does not match the method name. `TapOptional::tap_some`
+has no effect when called on a `None`, etc.
+
+<!-- Badges -->
+[crate]: https://crates.io/crates/tap "Crate Link"
+[crate_img]: https://img.shields.io/crates/v/tap.svg?logo=rust "Crate Page"
+[docs]: https://docs.rs/tap "Documentation"
+[docs_img]: https://docs.rs/tap/badge.svg "Documentation Display"
+[downloads_img]: https://img.shields.io/crates/dv/tap.svg?logo=rust "Crate Downloads"
+[license_file]: https://github.com/myrrlyn/tap/blob/master/LICENSE.txt "License File"
+[license_img]: https://img.shields.io/crates/l/tap.svg "License Display"
+[loc]: https://github.com/myrrlyn/tap "Repository"
+[loc_img]: https://tokei.rs/b1/github/myrrlyn/tap?category=code "Repository Size"
diff --git a/src/conv.rs b/src/conv.rs
new file mode 100644 (file)
index 0000000..cec1c85
--- /dev/null
@@ -0,0 +1,87 @@
+/*! # Method-Directed Type Conversion
+
+The `std::convert` module provides traits for converting values from one type to
+another. The first of these, [`From<T>`], provides an associated function
+[`from(orig: T) -> Self`]. This function can only be called in prefix-position,
+as it does not have a `self` receiver. The second, [`Into<T>`], provides a
+method [`into(self) -> T`] which *can* be called in suffix-position; due to
+intractable problems in the type solver, this method cannot have any *further*
+method calls attached to it. It must be bound directly into a `let` or function
+call.
+
+The [`TryFrom<T>`] and [`TryInto<T>`] traits have the same properties, but
+permit failure.
+
+This module provides traits that place the conversion type parameter in the
+method, rather than in the trait, so that users can write `.conv::<T>()` to
+convert the preceding expression into `T`, without causing any failures in the
+type solver. These traits are blanket-implemented on all types that have an
+`Into<T>` implementation, which covers both the blanket implementation of `Into`
+for types with `From`, and manual implementations of `Into`.
+
+[`From<T>`]: https://doc.rust-lang.org/std/convert/trait.From.html
+[`Into<T>`]: https://doc.rust-lang.org/std/convert/trait.Into.html
+[`TryFrom<T>`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html
+[`TryInto<T>`]: https://doc.rust-lang.org/std/convert/trait.TryInto.html
+[`from(orig: T) -> Self`]: https://doc.rust-lang.org/std/convert/trait.From.html#tymethod.from
+[`into(self) -> T`]: https://doc.rust-lang.org/std/convert/trait.Into.html#tymethod.into
+!*/
+
+use core::convert::TryInto;
+
+/// Wraps `Into::<T>::into` as a method that can be placed in pipelines.
+pub trait Conv
+where
+       Self: Sized,
+{
+       /// Converts `self` into `T` using `Into<T>`.
+       ///
+       /// # Examples
+       ///
+       /// ```rust
+       /// use tap::conv::Conv;
+       ///
+       /// let len = "Saluton, mondo!"
+       ///   .conv::<String>()
+       ///   .len();
+       /// ```
+       #[inline(always)]
+       fn conv<T>(self) -> T
+       where
+               Self: Into<T>,
+               T: Sized,
+       {
+               Into::<T>::into(self)
+       }
+}
+
+impl<T> Conv for T {}
+
+/// Wraps `TryInto::<T>::try_into` as a method that can be placed in pipelines.
+pub trait TryConv
+where
+       Self: Sized,
+{
+       /// Attempts to convert `self` into `T` using `TryInto<T>`.
+       ///
+       /// # Examples
+       ///
+       /// ```rust
+       /// use tap::conv::TryConv;
+       ///
+       /// let len = "Saluton, mondo!"
+       ///   .try_conv::<String>()
+       ///   .unwrap()
+       ///   .len();
+       /// ```
+       #[inline(always)]
+       fn try_conv<T>(self) -> Result<T, Self::Error>
+       where
+               Self: TryInto<T>,
+               T: Sized,
+       {
+               TryInto::<T>::try_into(self)
+       }
+}
+
+impl<T> TryConv for T {}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644 (file)
index 0000000..e6abc40
--- /dev/null
@@ -0,0 +1,147 @@
+/*! # `tap` – Syntactical Plumb-Lines
+
+Rust permits functions that take a `self` receiver to be written in “dot-call”
+suffix position, rather than the more traditional prefix-position function call
+syntax. These functions are restricted to `impl [Trait for] Type` blocks, and
+functions anywhere else cannot take advantage of this syntax.
+
+This crate provides universally-implemented extension traits that permit smooth
+suffix-position calls for a handful of common operations: transparent inspection
+or modification (tapping), transformation (piping), and type conversion.
+
+## Tapping
+
+The [`tap`] module provides the [`Tap`], [`TapOptional`], and [`TapFallible`]
+traits. Each of these traits provides methods that take and return a value, and
+expose it as a borrow to an effect function. They look like this:
+
+```rust
+use tap::prelude::*;
+# struct Tmp;
+# fn make_value() -> Tmp { Tmp }
+# impl Tmp { fn process_value(self) {} }
+# macro_rules! log { ($msg:literal, $val:ident) => {{}}; }
+
+let end = make_value()
+  .tap(|v| log!("Produced value: {:?}", v))
+  .process_value();
+```
+
+These methods are `self -> Self`, and return the value they received without
+any transformation. This enables them to be placed anywhere in a larger
+expression witohut changing its shape, or causing any semantic changes to the
+code. The effect function receives a borrow of the tapped value, optionally run
+through the `Borrow`, `AsRef`, or `Deref` view conversions, for the duration of
+its execution.
+
+The effect function cannot return a value, as the tap is incapable of handling
+it.
+
+## Piping
+
+The [`pipe`] module provides the [`Pipe`] trait. This trait provides methods
+that take and transform a value, returning the result of the transformation.
+They look like this:
+
+```rust
+use tap::prelude::*;
+
+struct One;
+fn start() -> One { One }
+struct Two;
+fn end(_: One) -> Two { Two }
+
+let val: Two = start().pipe(end);
+
+// without pipes, this would be written as
+let _: Two = end(start());
+```
+
+These methods are `self -> Other`, and return the value produced by the effect
+function. As the methods are always available in suffix position, they can take
+as arguments methods that are *not* eligible for dot-call syntax and still place
+them as expression suffices. The effect function receives the piped value,
+optionally run through the `Borrow`, `AsRef`, or `Deref` view conversions, as
+its input, and its output is returned from the pipe.
+
+For `.pipe()`, the input value is *moved* into the pipe and the effect function,
+so the effect function *cannot* return a value whose lifetime depends on the
+input value. The other pipe methods all borrow the input value, and may return a
+value whose lifetime is tied to it.
+
+## Converting
+
+The [`conv`] module provides the [`Conv`] and [`TryConv`] traits. These provide
+methods that accept a type parameter on the method name, and forward to the
+appropriate `Into` or `TryInto` trait implementation when called. The difference
+between `Conv` and `Into` is that `Conv` is declared as `Conv::conv::<T>`, while
+`Into` is declared as `Into::<T>::into`. The location of the destination type
+parameter makes `.into()` unusable as a non-terminal method call of an
+expression, while `.conv::<T>()` can be used as a method call anywhere.
+
+```rust,compile_fail
+let upper = "hello, world"
+  .into()
+  .tap_mut(|s| s.make_ascii_uppercase());
+```
+
+The above snippet is illegal, because the Rust type solver cannot determine the
+type of the sub-expression `"hello, world".into()`, and it will not attempt to
+search all available `impl Into<X> for str` implementations to find an `X` which
+has a
+`fn tap_mut({self, &self, &mut self, Box<Self>, Rc<Self>, Arc<Self>}, _) -> Y`
+declared, either as an inherent method or in a trait implemented by `X`, to
+resolve the expression.
+
+Instead, you can write it as
+
+```rust
+use tap::prelude::*;
+
+let upper = "hello, world"
+  .conv::<String>()
+  .tap_mut(|s| s.make_ascii_uppercase());
+```
+
+The trait implementation is
+
+```rust
+pub trait Conv: Sized {
+ fn conv<T: Sized>(self) -> T
+ where Self: Into<T> {
+  self.into()
+ }
+}
+```
+
+Each monomorphization of `.conv::<T>()` expands to the appropriate `Into<T>`
+implementation, and does nothing else.
+
+[`Conv`]: conv/trait.Conv.html
+[`Pipe`]: pipe/trait.Pipe.html
+[`Tap`]: tap/trait.Tap.html
+[`TapFallible`]: tap/trait.TapFallible.html
+[`TapOptional`]: tap/trait.TapOptional.html
+[`TryConv`]: conv/trait.TryConv.html
+[`conv`]: conv/index.html
+[`pipe`]: pipe/index.html
+[`tap`]: tap/index.html
+!*/
+
+#![no_std]
+#![cfg_attr(debug_assertions, warn(missing_docs))]
+#![cfg_attr(not(debug_assertions), deny(missing_docs))]
+
+pub mod conv;
+pub mod pipe;
+pub mod tap;
+
+/// Reëxports all traits in one place, for easy import.
+pub mod prelude {
+       #[doc(inline)]
+       pub use crate::{conv::*, pipe::*, tap::*};
+}
+
+// also make traits available at crate root
+#[doc(inline)]
+pub use prelude::*;
diff --git a/src/pipe.rs b/src/pipe.rs
new file mode 100644 (file)
index 0000000..32cba57
--- /dev/null
@@ -0,0 +1,234 @@
+/*! # Universal Suffix Calls
+
+This module provides a single trait, `Pipe`, which provides a number of methods
+useful for placing functions in suffix position. The most common method, `pipe`,
+forwards a value `T` into any function `T -> R`, returning `R`. The other
+methods all apply some form of borrowing to the value before passing the borrow
+into the piped function. These are of less value, but provided to maintain a
+similar API to the `tap` module’s methods, and for convenience in the event that
+you do have a use for them.
+
+This module is as much of a [UFCS] method syntax that can be provided as a
+library, rather than in the language grammar.
+
+[UFCS]: https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax
+!*/
+
+use core::{
+       borrow::{Borrow, BorrowMut},
+       ops::{Deref, DerefMut},
+};
+
+/** Provides universal suffix-position call syntax for any function.
+
+This trait provides methods that allow any closure or free function to be placed
+as a suffix-position call, by writing them as
+
+```rust
+# use tap::pipe::Pipe;
+# let receiver = 5;
+fn not_a_method(x: i32) -> u8 { x as u8 }
+receiver.pipe(not_a_method);
+```
+
+Piping into functions that take more than one argument still requires writing a
+closure with ordinary function-call syntax. This is after all only a library,
+not a syntax transformation:
+
+```rust
+use tap::pipe::Pipe;
+fn add(x: i32, y: i32) -> i32 { x + y }
+
+let out = 5.pipe(|x| add(x, 10));
+assert_eq!(out, 15);
+```
+
+Like tapping, piping is useful for cases where you want to write a sequence of
+processing steps without introducing many intermediate bindings, and your steps
+contain functions which are not eligible for dot-call syntax.
+
+The main difference between piping and tapping is that tapping always returns
+the value that was passed into the tap, while piping forwards the value into the
+effect function, and returns the output of evaluating the effect function with
+the value. Piping is a transformation, not merely an inspection or modification.
+**/
+pub trait Pipe {
+       /// Pipes by value. This is generally the method you want to use.
+       ///
+       /// # Examples
+       ///
+       /// ```rust
+       /// use tap::pipe::Pipe;
+       ///
+       /// fn triple(x: i32) -> i64 {
+       ///   x as i64 * 3
+       /// }
+       ///
+       /// assert_eq!(
+       ///   10.pipe(triple),
+       ///   30,
+       /// );
+       /// ```
+       #[inline(always)]
+       fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
+       where
+               Self: Sized,
+               R: Sized,
+       {
+               func(self)
+       }
+
+       /// Borrows `self` and passes that borrow into the pipe function.
+       ///
+       /// # Examples
+       ///
+       /// ```rust
+       /// use tap::pipe::Pipe;
+       ///
+       /// fn fold(v: &Vec<i32>) -> i32 {
+       ///   v.iter().copied().sum()
+       /// }
+       /// let vec = vec![1, 2, 3, 4, 5];
+       /// let sum = vec.pipe_ref(fold);
+       /// assert_eq!(sum, 15);
+       /// assert_eq!(vec.len(), 5);
+       /// ```
+       #[inline(always)]
+       fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
+       where
+               R: 'a + Sized,
+       {
+               func(self)
+       }
+
+       /// Mutably borrows `self` and passes that borrow into the pipe function.
+       ///
+       /// # Examples
+       ///
+       /// ```rust
+       /// use tap::pipe::Pipe;
+       ///
+       /// let mut vec = vec![false, true];
+       /// let last = vec
+       ///   .pipe_ref_mut(Vec::pop)
+       ///   .pipe(Option::unwrap);
+       /// assert!(last);
+       /// ```
+       ///
+       /// Both of these functions are eligible for method-call syntax, and should
+       /// not be piped. Writing out non-trivial examples for these is a lot of
+       /// boilerplate.
+       #[inline(always)]
+       fn pipe_ref_mut<'a, R>(
+               &'a mut self,
+               func: impl FnOnce(&'a mut Self) -> R,
+       ) -> R
+       where
+               R: 'a + Sized,
+       {
+               func(self)
+       }
+
+       /// Borrows `self`, then passes `self.borrow()` into the pipe function.
+       ///
+       /// # Examples
+       ///
+       /// ```rust
+       /// use std::borrow::Cow;
+       /// use tap::pipe::Pipe;
+       ///
+       /// let len = Cow::<'static, str>::from("hello, world")
+       ///   .pipe_borrow(str::len);
+       /// assert_eq!(len, 12);
+       /// ```
+       #[inline(always)]
+       fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
+       where
+               Self: Borrow<B>,
+               B: 'a + ?Sized,
+               R: 'a + Sized,
+       {
+               func(Borrow::<B>::borrow(self))
+       }
+
+       /// Mutably borrows `self`, then passes `self.borrow_mut()` into the pipe
+       /// function.
+       ///
+       /// ```rust
+       /// use tap::pipe::Pipe;
+       ///
+       /// let mut txt = "hello, world".to_string();
+       /// let ptr = txt
+       ///   .pipe_borrow_mut(str::as_mut_ptr);
+       /// ```
+       ///
+       /// This is a very contrived example, but the `BorrowMut` trait has almost
+       /// no implementors in the standard library, and of the implementations
+       /// available, there are almost no methods that fit this API.
+       #[inline(always)]
+       fn pipe_borrow_mut<'a, B, R>(
+               &'a mut self,
+               func: impl FnOnce(&'a mut B) -> R,
+       ) -> R
+       where
+               Self: BorrowMut<B>,
+               B: 'a + ?Sized,
+               R: 'a + Sized,
+       {
+               func(BorrowMut::<B>::borrow_mut(self))
+       }
+
+       /// Borrows `self`, then passes `self.as_ref()` into the pipe function.
+       #[inline(always)]
+       fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
+       where
+               Self: AsRef<U>,
+               U: 'a + ?Sized,
+               R: 'a + Sized,
+       {
+               func(AsRef::<U>::as_ref(self))
+       }
+
+       /// Mutably borrows `self`, then passes `self.as_mut()` into the pipe
+       /// function.
+       #[inline(always)]
+       fn pipe_as_mut<'a, U, R>(
+               &'a mut self,
+               func: impl FnOnce(&'a mut U) -> R,
+       ) -> R
+       where
+               Self: AsMut<U>,
+               U: 'a + ?Sized,
+               R: 'a + Sized,
+       {
+               func(AsMut::<U>::as_mut(self))
+       }
+
+       /// Borrows `self`, then passes `self.deref()` into the pipe function.
+       #[inline(always)]
+       fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
+       where
+               Self: Deref<Target = T>,
+               T: 'a + ?Sized,
+               R: 'a + Sized,
+       {
+               func(Deref::deref(self))
+       }
+
+       /// Mutably borrows `self`, then passes `self.deref_mut()` into the pipe
+       /// function.
+       #[inline(always)]
+       fn pipe_deref_mut<'a, T, R>(
+               &'a mut self,
+               func: impl FnOnce(&'a mut T) -> R,
+       ) -> R
+       where
+               Self: DerefMut + Deref<Target = T>,
+               T: 'a + ?Sized,
+               R: 'a + Sized,
+       {
+               func(DerefMut::deref_mut(self))
+       }
+}
+
+impl<T> Pipe for T where T: ?Sized {}
diff --git a/src/tap.rs b/src/tap.rs
new file mode 100644 (file)
index 0000000..50ce864
--- /dev/null
@@ -0,0 +1,587 @@
+/*! # Point-Free Inspection
+
+The standard library does not provide a way to view or modify an expression
+without binding it to a name. This module provides extension methods that take
+and return a value, allowing it to be temporarily bound without creating a new
+`let`-statement in the enclosing scope.
+
+The two main uses of these methods are to temporarily attach debugging
+tracepoints to an expression without modifying its surrounding code, or to
+temporarily mutate an otherwise-immutable object.
+
+For convenience, methods are available that will modify the *view* of the tapped
+object that is passed to the effect function, by using the value’s
+`Borrow`/`BorrowMut`, `AsRef`/`AsMut`, or `Index`/`IndexMut` trait
+implementations. For example, the `Vec` collection has no `fn sort` method: this
+is actually implemented on slices, to which `Vec` dereferences.
+
+```rust
+use tap::tap::*;
+# fn make_vec() -> Vec<i32> { vec![] }
+
+// taps take ordinary closures, which can use deref coercion
+make_vec().tap_mut(|v| v.sort());
+// `Vec<T>` implements `BorrowMut<[T]>`,
+make_vec().tap_borrow_mut(<[_]>::sort);
+// and `AsMut<[T]>`,
+make_vec().tap_ref_mut(<[_]>::sort);
+// and `DerefMut<Target = [T]>,
+make_vec().tap_deref_mut(<[_]>::sort);
+// but has no inherent method `sort`.
+// make_vec().tap_mut(Vec::sort);
+```
+!*/
+
+use core::{
+       borrow::{Borrow, BorrowMut},
+       ops::{Deref, DerefMut},
+};
+
+/** Point-free value inspection and modification.
+
+This trait provides methods that permit viewing the value of an expression
+without requiring a new `let` binding or any other alterations to the original
+code other than insertion of the `.tap()` call.
+
+The methods in this trait do not perform any view conversions on the value they
+receive; it is borrowed and passed directly to the effect argument.
+**/
+pub trait Tap
+where
+       Self: Sized,
+{
+       /// Immutable access to a value.
+       ///
+       /// This function permits a value to be viewed by some inspecting function
+       /// without affecting the overall shape of the expression that contains this
+       /// method call. It is useful for attaching assertions or logging points
+       /// into a multi-part expression.
+       ///
+       /// # Examples
+       ///
+       /// Here we use `.tap()` to attach logging tracepoints to each stage of a
+       /// value-processing pipeline.
+       ///
+       /// ```rust
+       /// use tap::tap::Tap;
+       /// # struct Tmp;
+       /// # impl Tmp { fn process_value(self) -> Self { self } }
+       /// # fn make_value() -> Tmp { Tmp }
+       /// # macro_rules! log { ($msg:literal, $x:ident) => {{}}; }
+       ///
+       /// let end = make_value()
+       ///   // this line has no effect on the rest of the code
+       ///   .tap(|v| log!("The produced value was: {}", v))
+       ///   .process_value();
+       /// ```
+       #[inline(always)]
+       fn tap(self, func: impl FnOnce(&Self)) -> Self {
+               func(&self);
+               self
+       }
+
+       /// Mutable access to a value.
+       ///
+       /// This function permits a value to be modified by some function without
+       /// affecting the overall shape of the expression that contains this method
+       /// call. It is useful for attaching modifier functions that have an
+       /// `&mut Self -> ()` signature to an expression, without requiring an
+       /// explicit `let mut` binding.
+       ///
+       /// # Examples
+       ///
+       /// Here we use `.tap_mut()` to sort an array without requring multiple
+       /// bindings.
+       ///
+       /// ```rust
+       /// use tap::tap::Tap;
+       ///
+       /// let sorted = [1i32, 5, 2, 4, 3]
+       ///   .tap_mut(|arr| arr.sort());
+       /// assert_eq!(sorted, [1, 2, 3, 4, 5]);
+       /// ```
+       ///
+       /// Without tapping, this would be written as
+       ///
+       /// ```rust
+       /// let mut received = [1, 5, 2, 4, 3];
+       /// received.sort();
+       /// let sorted = received;
+       /// ```
+       ///
+       /// The mutable tap is a convenient alternative when the expression to
+       /// produce the collection is more complex, for example, an iterator
+       /// pipeline collected into a vector.
+       #[inline(always)]
+       fn tap_mut(mut self, func: impl FnOnce(&mut Self)) -> Self {
+               func(&mut self);
+               self
+       }
+
+       /// Immutable access to the `Borrow<B>` of a value.
+       ///
+       /// This function is identcal to [`Tap::tap`], except that the effect
+       /// function recevies an `&B` produced by `Borrow::<B>::borrow`, rather than
+       /// an `&Self`.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       #[inline(always)]
+       fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
+       where
+               Self: Borrow<B>,
+               B: ?Sized,
+       {
+               func(Borrow::<B>::borrow(&self));
+               self
+       }
+
+       /// Mutable access to the `BorrowMut<B>` of a value.
+       ///
+       /// This function is identical to [`Tap::tap_mut`], except that the effect
+       /// function receives an `&mut B` produced by `BorrowMut::<B>::borrow_mut`,
+       /// rather than an `&mut Self`.
+       ///
+       /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
+       #[inline(always)]
+       fn tap_borrow_mut<B>(mut self, func: impl FnOnce(&mut B)) -> Self
+       where
+               Self: BorrowMut<B>,
+               B: ?Sized,
+       {
+               func(BorrowMut::<B>::borrow_mut(&mut self));
+               self
+       }
+
+       /// Immutable access to the `AsRef<R>` view of a value.
+       ///
+       /// This function is identical to [`Tap::tap`], except that the effect
+       /// function receives an `&R` produced by `AsRef::<R>::as_ref`, rather than
+       /// an `&Self`.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       #[inline(always)]
+       fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
+       where
+               Self: AsRef<R>,
+               R: ?Sized,
+       {
+               func(AsRef::<R>::as_ref(&self));
+               self
+       }
+
+       /// Mutable access to the `AsMut<R>` view of a value.
+       ///
+       /// This function is identical to [`Tap::tap_mut`], except that the effect
+       /// function receives an `&mut R` produced by `AsMut::<R>::as_mut`, rather
+       /// than an `&mut Self`.
+       ///
+       /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
+       #[inline(always)]
+       fn tap_ref_mut<R>(mut self, func: impl FnOnce(&mut R)) -> Self
+       where
+               Self: AsMut<R>,
+               R: ?Sized,
+       {
+               func(AsMut::<R>::as_mut(&mut self));
+               self
+       }
+
+       /// Immutable access to the `Deref::Target` of a value.
+       ///
+       /// This function is identical to [`Tap::tap`], except that the effect
+       /// function receives an `&Self::Target` produced by `Deref::deref`, rather
+       /// than an `&Self`.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       #[inline(always)]
+       fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
+       where
+               Self: Deref<Target = T>,
+               T: ?Sized,
+       {
+               func(Deref::deref(&self));
+               self
+       }
+
+       /// Mutable access to the `Deref::Target` of a value.
+       ///
+       /// This function is identical to [`Tap::tap_mut`], except that the effect
+       /// function receives an `&mut Self::Target` produced by
+       /// `DerefMut::deref_mut`, rather than an `&mut Self`.
+       ///
+       /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
+       #[inline(always)]
+       fn tap_deref_mut<T>(mut self, func: impl FnOnce(&mut T)) -> Self
+       where
+               Self: DerefMut + Deref<Target = T>,
+               T: ?Sized,
+       {
+               func(DerefMut::deref_mut(&mut self));
+               self
+       }
+
+       //  debug-build-only copies of the above methods
+
+       /// Calls `.tap()` only in debug builds, and is erased in release builds.
+       #[inline(always)]
+       fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self {
+               if cfg!(debug_assertions) {
+                       func(&self);
+               }
+               self
+       }
+
+       /// Calls `.tap_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_mut_dbg(mut self, func: impl FnOnce(&mut Self)) -> Self {
+               if cfg!(debug_assertions) {
+                       func(&mut self);
+               }
+               self
+       }
+
+       /// Calls `.tap_borrow()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
+       where
+               Self: Borrow<B>,
+               B: ?Sized,
+       {
+               if cfg!(debug_assertions) {
+                       func(Borrow::<B>::borrow(&self));
+               }
+               self
+       }
+
+       /// Calls `.tap_borrow_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_borrow_mut_dbg<B>(mut self, func: impl FnOnce(&mut B)) -> Self
+       where
+               Self: BorrowMut<B>,
+               B: ?Sized,
+       {
+               if cfg!(debug_assertions) {
+                       func(BorrowMut::<B>::borrow_mut(&mut self));
+               }
+               self
+       }
+
+       /// Calls `.tap_ref()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
+       where
+               Self: AsRef<R>,
+               R: ?Sized,
+       {
+               if cfg!(debug_assertions) {
+                       func(AsRef::<R>::as_ref(&self));
+               }
+               self
+       }
+
+       /// Calls `.tap_ref_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_ref_mut_dbg<R>(mut self, func: impl FnOnce(&mut R)) -> Self
+       where
+               Self: AsMut<R>,
+               R: ?Sized,
+       {
+               if cfg!(debug_assertions) {
+                       func(AsMut::<R>::as_mut(&mut self));
+               }
+               self
+       }
+
+       /// Calls `.tap_deref()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
+       where
+               Self: Deref<Target = T>,
+               T: ?Sized,
+       {
+               if cfg!(debug_assertions) {
+                       func(Deref::deref(&self));
+               }
+               self
+       }
+
+       /// Calls `.tap_deref_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_deref_mut_dbg<T>(mut self, func: impl FnOnce(&mut T)) -> Self
+       where
+               Self: DerefMut + Deref<Target = T>,
+               T: ?Sized,
+       {
+               if cfg!(debug_assertions) {
+                       func(DerefMut::deref_mut(&mut self));
+               }
+               self
+       }
+}
+
+impl<T> Tap for T where T: Sized {}
+
+/** Optional tapping, conditional on the optional presence of a value.
+
+This trait is intended for use on types that express the concept of “optional
+presence”, primarily the [`Option`] monad. It provides taps that inspect the
+container to determine if the effect function should execute or not.
+
+> Note: This trait is a specialization of [`TapFallible`], and exists because
+> the [`std::ops::Try`] trait is still unstable. When `Try` stabilizes, this
+> trait can be removed, and `TapFallible` blanket-applied to all `Try`
+> implementors.
+
+[`Option`]: https://doc.rust-lang.org/std/option/enum.Option.html
+[`TapFallible`]: trait.TapFallible.html
+[`std::ops::Try`]: https://doc.rust-lang.org/std/ops/trait.Try.html
+**/
+pub trait TapOptional
+where
+       Self: Sized,
+{
+       /// The interior type that the container may or may not carry.
+       type Val: ?Sized;
+
+       /// Immutabily accesses an interior value only when it is present.
+       ///
+       /// This function is identical to [`Tap::tap`], except that it is required
+       /// to check the implementing container for value presence before running.
+       /// Implementors must not run the effect function if the container is marked
+       /// as being empty.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       fn tap_some(self, func: impl FnOnce(&Self::Val)) -> Self;
+
+       /// Mutably accesses an interor value only when it is present.
+       ///
+       /// This function is identical to [`Tap::tap_mut`], except that it is
+       /// required to check the implementing container for value presence before
+       /// running. Implementors must not run the effect function if the container
+       /// is marked as being empty.
+       ///
+       /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
+       fn tap_some_mut(self, func: impl FnOnce(&mut Self::Val)) -> Self;
+
+       /// Runs an effect function when the container is empty.
+       ///
+       /// This function is identical to [`Tap::tap`], except that it is required
+       /// to check the implementing container for value absence before running.
+       /// Implementors must not run the effect function if the container is marked
+       /// as being non-empty.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       fn tap_none(self, func: impl FnOnce()) -> Self;
+
+       /// Calls `.tap_some()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_some_dbg(self, func: impl FnOnce(&Self::Val)) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_some(func)
+               } else {
+                       self
+               }
+       }
+
+       /// Calls `.tap_some_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_some_mut_dbg(self, func: impl FnOnce(&mut Self::Val)) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_some_mut(func)
+               } else {
+                       self
+               }
+       }
+
+       /// Calls `.tap_none()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_none_dbg(self, func: impl FnOnce()) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_none(func)
+               } else {
+                       self
+               }
+       }
+}
+
+impl<T> TapOptional for Option<T> {
+       type Val = T;
+
+       #[inline(always)]
+       fn tap_some(self, func: impl FnOnce(&T)) -> Self {
+               if let Some(ref val) = self {
+                       func(val);
+               }
+               self
+       }
+
+       #[inline(always)]
+       fn tap_some_mut(mut self, func: impl FnOnce(&mut T)) -> Self {
+               if let Some(ref mut val) = self {
+                       func(val);
+               }
+               self
+       }
+
+       #[inline(always)]
+       fn tap_none(self, func: impl FnOnce()) -> Self {
+               if self.is_none() {
+                       func();
+               }
+               self
+       }
+}
+
+/** Fallible tapping, conditional on the optional success of an expression.
+
+This trait is intended for use on types that express the concept of “fallible
+presence”, primarily the [`Result`] monad. It provides taps that inspect the
+container to determine if the effect function should execute or not.
+
+> Note: This trait would ideally be implemented as a blanket over all
+> [`std::ops::Try`] implementors. When `Try` stabilizes, this crate can be
+> updated to do so.
+
+[`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
+[`std::ops::Try`]: https://doc.rust-lang.org/std/ops/trait.Try.html
+**/
+pub trait TapFallible
+where
+       Self: Sized,
+{
+       /// The interior type used to indicate a successful construction.
+       type Ok: ?Sized;
+
+       /// The interior type used to indicate a failed construction.
+       type Err: ?Sized;
+
+       /// Immutably accesses an interior success value.
+       ///
+       /// This function is identical to [`Tap::tap`], except that it is required
+       /// to check the implementing container for value success before running.
+       /// Implementors must not run the effect function if the container is marked
+       /// as being a failure.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       fn tap_ok(self, func: impl FnOnce(&Self::Ok)) -> Self;
+
+       /// Mutably accesses an interior success value.
+       ///
+       /// This function is identical to [`Tap::tap_mut`], except that it is
+       /// required to check the implementing container for value success before
+       /// running. Implementors must not run the effect function if the container
+       /// is marked as being a failure.
+       ///
+       /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
+       fn tap_ok_mut(self, func: impl FnOnce(&mut Self::Ok)) -> Self;
+
+       /// Immutably accesses an interior failure value.
+       ///
+       /// This function is identical to [`Tap::tap`], except that it is required
+       /// to check the implementing container for value failure before running.
+       /// Implementors must not run the effect function if the container is marked
+       /// as being a success.
+       ///
+       /// [`Tap::tap`]: trait.Tap.html#method.tap
+       fn tap_err(self, func: impl FnOnce(&Self::Err)) -> Self;
+
+       /// Mutably accesses an interior failure value.
+       ///
+       /// This function is identical to [`Tap::tap_mut`], except that it is
+       /// required to check the implementing container for value failure before
+       /// running. Implementors must not run the effect function if the container
+       /// is marked as being a success.
+       ///
+       /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut
+       fn tap_err_mut(self, func: impl FnOnce(&mut Self::Err)) -> Self;
+
+       /// Calls `.tap_ok()` only in debug builds, and is erased in release builds.
+       #[inline(always)]
+       fn tap_ok_dbg(self, func: impl FnOnce(&Self::Ok)) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_ok(func)
+               } else {
+                       self
+               }
+       }
+
+       /// Calls `.tap_ok_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_ok_mut_dbg(self, func: impl FnOnce(&mut Self::Ok)) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_ok_mut(func)
+               } else {
+                       self
+               }
+       }
+
+       /// Calls `.tap_err()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_err_dbg(self, func: impl FnOnce(&Self::Err)) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_err(func)
+               } else {
+                       self
+               }
+       }
+
+       /// Calls `.tap_err_mut()` only in debug builds, and is erased in release
+       /// builds.
+       #[inline(always)]
+       fn tap_err_mut_dbg(self, func: impl FnOnce(&mut Self::Err)) -> Self {
+               if cfg!(debug_assertions) {
+                       self.tap_err_mut(func)
+               } else {
+                       self
+               }
+       }
+}
+
+impl<T, E> TapFallible for Result<T, E> {
+       type Ok = T;
+       type Err = E;
+
+       #[inline(always)]
+       fn tap_ok(self, func: impl FnOnce(&T)) -> Self {
+               if let Ok(ref val) = self {
+                       func(val);
+               }
+               self
+       }
+
+       #[inline(always)]
+       fn tap_ok_mut(mut self, func: impl FnOnce(&mut T)) -> Self {
+               if let Ok(ref mut val) = self {
+                       func(val);
+               }
+               self
+       }
+
+       #[inline(always)]
+       fn tap_err(self, func: impl FnOnce(&E)) -> Self {
+               if let Err(ref val) = self {
+                       func(val);
+               }
+               self
+       }
+
+       #[inline(always)]
+       fn tap_err_mut(mut self, func: impl FnOnce(&mut E)) -> Self {
+               if let Err(ref mut val) = self {
+                       func(val);
+               }
+               self
+       }
+}