tap-1.0.1/.cargo_vcs_info.json0000644000000001120000000000000116100ustar { "git": { "sha1": "f5315f0f5ca90ce6399daac76c1fe3ba645a4e4e" } } tap-1.0.1/Cargo.toml0000644000000020340000000000000076130ustar # 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 ", "myrrlyn "] 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" tap-1.0.1/Cargo.toml.orig000064400000000000000000000010500000000000000132470ustar 00000000000000[package] authors = [ "Elliott Linder ", "myrrlyn ", ] 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" tap-1.0.1/LICENSE.txt000064400000000000000000000021020000000000000122020ustar 00000000000000MIT License Copyright (c) 2017 Elliot Linder 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. tap-1.0.1/README.md000064400000000000000000000151150000000000000116460ustar 00000000000000
# `tap` ## Suffix-Position Pipeline Behavior [![Crate][crate_img]][crate] [![Documentation][docs_img]][docs] [![License][license_img]][license_file] [![Crate Downloads][downloads_img]][crate] [![Crate Size][loc_img]][loc]
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::>(); 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::>() .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` 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` and `TryInto`. Their methods, `Conv::conv::` and `TryConv::try_conv::`, 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::().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. [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" tap-1.0.1/src/conv.rs000064400000000000000000000050520000000000000124700ustar 00000000000000/*! # Method-Directed Type Conversion The `std::convert` module provides traits for converting values from one type to another. The first of these, [`From`], 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`], 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`] and [`TryInto`] 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::()` 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` implementation, which covers both the blanket implementation of `Into` for types with `From`, and manual implementations of `Into`. [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html [`Into`]: https://doc.rust-lang.org/std/convert/trait.Into.html [`TryFrom`]: https://doc.rust-lang.org/std/convert/trait.TryFrom.html [`TryInto`]: 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::::into` as a method that can be placed in pipelines. pub trait Conv where Self: Sized, { /// Converts `self` into `T` using `Into`. /// /// # Examples /// /// ```rust /// use tap::conv::Conv; /// /// let len = "Saluton, mondo!" /// .conv::() /// .len(); /// ``` #[inline(always)] fn conv(self) -> T where Self: Into, T: Sized, { Into::::into(self) } } impl Conv for T {} /// Wraps `TryInto::::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`. /// /// # Examples /// /// ```rust /// use tap::conv::TryConv; /// /// let len = "Saluton, mondo!" /// .try_conv::() /// .unwrap() /// .len(); /// ``` #[inline(always)] fn try_conv(self) -> Result where Self: TryInto, T: Sized, { TryInto::::try_into(self) } } impl TryConv for T {} tap-1.0.1/src/lib.rs000064400000000000000000000114110000000000000122650ustar 00000000000000/*! # `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::`, while `Into` is declared as `Into::::into`. The location of the destination type parameter makes `.into()` unusable as a non-terminal method call of an expression, while `.conv::()` 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 for str` implementations to find an `X` which has a `fn tap_mut({self, &self, &mut self, Box, Rc, Arc}, _) -> 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::() .tap_mut(|s| s.make_ascii_uppercase()); ``` The trait implementation is ```rust pub trait Conv: Sized { fn conv(self) -> T where Self: Into { self.into() } } ``` Each monomorphization of `.conv::()` expands to the appropriate `Into` 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::*; tap-1.0.1/src/pipe.rs000064400000000000000000000133470000000000000124660ustar 00000000000000/*! # 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(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 { /// 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: 'a + ?Sized, R: 'a + Sized, { func(Borrow::::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: 'a + ?Sized, R: 'a + Sized, { func(BorrowMut::::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: 'a + ?Sized, R: 'a + Sized, { func(AsRef::::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: 'a + ?Sized, R: 'a + Sized, { func(AsMut::::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, 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, T: 'a + ?Sized, R: 'a + Sized, { func(DerefMut::deref_mut(self)) } } impl Pipe for T where T: ?Sized {} tap-1.0.1/src/tap.rs000064400000000000000000000401700000000000000123070ustar 00000000000000/*! # 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 { vec![] } // taps take ordinary closures, which can use deref coercion make_vec().tap_mut(|v| v.sort()); // `Vec` implements `BorrowMut<[T]>`, make_vec().tap_borrow_mut(<[_]>::sort); // and `AsMut<[T]>`, make_vec().tap_ref_mut(<[_]>::sort); // and `DerefMut, 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` of a value. /// /// This function is identcal to [`Tap::tap`], except that the effect /// function recevies an `&B` produced by `Borrow::::borrow`, rather than /// an `&Self`. /// /// [`Tap::tap`]: trait.Tap.html#method.tap #[inline(always)] fn tap_borrow(self, func: impl FnOnce(&B)) -> Self where Self: Borrow, B: ?Sized, { func(Borrow::::borrow(&self)); self } /// Mutable access to the `BorrowMut` of a value. /// /// This function is identical to [`Tap::tap_mut`], except that the effect /// function receives an `&mut B` produced by `BorrowMut::::borrow_mut`, /// rather than an `&mut Self`. /// /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut #[inline(always)] fn tap_borrow_mut(mut self, func: impl FnOnce(&mut B)) -> Self where Self: BorrowMut, B: ?Sized, { func(BorrowMut::::borrow_mut(&mut self)); self } /// Immutable access to the `AsRef` view of a value. /// /// This function is identical to [`Tap::tap`], except that the effect /// function receives an `&R` produced by `AsRef::::as_ref`, rather than /// an `&Self`. /// /// [`Tap::tap`]: trait.Tap.html#method.tap #[inline(always)] fn tap_ref(self, func: impl FnOnce(&R)) -> Self where Self: AsRef, R: ?Sized, { func(AsRef::::as_ref(&self)); self } /// Mutable access to the `AsMut` view of a value. /// /// This function is identical to [`Tap::tap_mut`], except that the effect /// function receives an `&mut R` produced by `AsMut::::as_mut`, rather /// than an `&mut Self`. /// /// [`Tap::tap_mut`]: trait.Tap.html#method.tap_mut #[inline(always)] fn tap_ref_mut(mut self, func: impl FnOnce(&mut R)) -> Self where Self: AsMut, R: ?Sized, { func(AsMut::::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(self, func: impl FnOnce(&T)) -> Self where Self: Deref, 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(mut self, func: impl FnOnce(&mut T)) -> Self where Self: DerefMut + Deref, 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(self, func: impl FnOnce(&B)) -> Self where Self: Borrow, B: ?Sized, { if cfg!(debug_assertions) { func(Borrow::::borrow(&self)); } self } /// Calls `.tap_borrow_mut()` only in debug builds, and is erased in release /// builds. #[inline(always)] fn tap_borrow_mut_dbg(mut self, func: impl FnOnce(&mut B)) -> Self where Self: BorrowMut, B: ?Sized, { if cfg!(debug_assertions) { func(BorrowMut::::borrow_mut(&mut self)); } self } /// Calls `.tap_ref()` only in debug builds, and is erased in release /// builds. #[inline(always)] fn tap_ref_dbg(self, func: impl FnOnce(&R)) -> Self where Self: AsRef, R: ?Sized, { if cfg!(debug_assertions) { func(AsRef::::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(mut self, func: impl FnOnce(&mut R)) -> Self where Self: AsMut, R: ?Sized, { if cfg!(debug_assertions) { func(AsMut::::as_mut(&mut self)); } self } /// Calls `.tap_deref()` only in debug builds, and is erased in release /// builds. #[inline(always)] fn tap_deref_dbg(self, func: impl FnOnce(&T)) -> Self where Self: Deref, 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(mut self, func: impl FnOnce(&mut T)) -> Self where Self: DerefMut + Deref, T: ?Sized, { if cfg!(debug_assertions) { func(DerefMut::deref_mut(&mut self)); } self } } impl 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 TapOptional for Option { 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 TapFallible for Result { 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 } }