├── .gitignore ├── .editorconfig ├── Cargo.toml ├── tests └── lib.rs ├── src ├── future.rs ├── nom.rs └── lib.rs └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.rs] 4 | end_of_line = lf 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 4 8 | insert_final_newline = true 9 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Elliott Linder ", 4 | "Alexander Payne ", 5 | ] 6 | description = "Generic extensions for tapping values in Rust" 7 | documentation = "https://docs.rs/tap" 8 | homepage = "https://github.com/darfink/tap-rs" 9 | keywords = ["tap", "functional", "tap_ok", "tap_some"] 10 | license = "MIT" 11 | name = "tap" 12 | readme = "README.md" 13 | repository = "https://github.com/darfink/tap-rs" 14 | version = "0.4.0" 15 | 16 | [package.metadata.docs.rs] 17 | features = ["future, nom3"] 18 | 19 | [dependencies.futures] 20 | optional = true 21 | version = "0.1.14" 22 | 23 | [dependencies.nom] 24 | optional = true 25 | version = "3" 26 | 27 | [features] 28 | default = [] 29 | future = ["futures"] 30 | nom3 = ["nom"] 31 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate tap; 2 | 3 | use tap::*; 4 | 5 | #[test] 6 | fn filter_map() { 7 | let values: &[Result] = &[Ok(3), Err("foo"), Err("bar"), Ok(8)]; 8 | let _ = values.iter().filter_map(|result| { 9 | // It is especially useful in filter maps, allowing error information to 10 | // be logged/printed before the information is discarded. 11 | result 12 | .tap_err(|error| println!("Invalid entry: {}", error)) 13 | .ok() 14 | }); 15 | } 16 | 17 | #[test] 18 | fn basic() { 19 | let mut foo = 5; 20 | 21 | // The `tap` extension can be used on all types 22 | if 10.tap(|v| foo += *v) > 0 { 23 | assert_eq!(foo, 15); 24 | } 25 | 26 | // Results have `tap_err` & `tap_ok` available. 27 | let _: Result = Err(5).tap_err(|e| foo = *e); 28 | assert_eq!(foo, 5); 29 | 30 | // Options have `tap_some` & `tap_none` available. 31 | let _: Option = None.tap_none(|| foo = 10); 32 | assert_eq!(foo, 10); 33 | } 34 | -------------------------------------------------------------------------------- /src/future.rs: -------------------------------------------------------------------------------- 1 | //! Tap operations for `Future`, requires the feature `future`. 2 | extern crate futures; 3 | 4 | use self::futures::{Async, Future}; 5 | 6 | /// Tap operations for `Future`, requires the feature `future`. 7 | pub trait TapFutureOps { 8 | /// Executes a closure if the value is `Async::Ready(T)`. 9 | fn tap_ready R>(self, f: F) -> Self; 10 | 11 | // Executes a closure if the value is `Async::NotReady`. 12 | fn tap_not_ready R>(self, f: F) -> Self; 13 | 14 | // Executes a closure if the value is `Err(E)`. 15 | fn tap_err R>(self, f: F) -> Self; 16 | } 17 | 18 | impl> TapFutureOps for FUT { 19 | fn tap_ready R>(mut self, f: F) -> Self { 20 | if let Ok(Async::Ready(ref val)) = self.poll() { 21 | let _ = f(val); 22 | } 23 | self 24 | } 25 | 26 | fn tap_not_ready R>(mut self, f: F) -> Self { 27 | if let Ok(Async::NotReady) = self.poll() { 28 | let _ = f(); 29 | } 30 | self 31 | } 32 | 33 | fn tap_err R>(mut self, f: F) -> Self { 34 | if let Err(ref val) = self.poll() { 35 | let _ = f(val); 36 | } 37 | self 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | 45 | #[test] 46 | fn ready() { 47 | let mut foo = 0; 48 | let future = futures::future::result::(Ok(5)); 49 | 50 | let _ = future.tap_ready(|x| foo += *x); 51 | assert_eq!(foo, 5); 52 | } 53 | 54 | #[test] 55 | fn not_ready() { 56 | let mut foo = 0; 57 | let future = futures::future::empty::(); 58 | 59 | match future.tap_not_ready(|| foo += 5).poll() { 60 | Ok(Async::NotReady) => assert_eq!(foo, 5), 61 | _ => unreachable!() 62 | } 63 | } 64 | 65 | #[test] 66 | fn error() { 67 | let mut foo = 0; 68 | let future = futures::future::result::(Err(5)); 69 | 70 | let _ = future.tap_err(|x| foo += *x); 71 | assert_eq!(foo, 5); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tap 2 | 3 | [![Documentation][docs-shield]][docs] 4 | [![crates.io version][crate-shield]][crate] 5 | [![Language (Rust)][rust-shield]][rust] 6 | 7 | A simple crate exposing tapping functionality for all types, and extended 8 | functionality for `Option`, `Result` & `Future`. Often useful for logging. 9 | 10 | The tap operation takes, and then returns, full ownership of the variable being 11 | tapped. This means that the closure may have mutable access to the variable, 12 | even if the variable was previously immutable. This prevents accidental mutation. 13 | 14 | ## Features 15 | 16 | * `future` - Exposes the `TapFutureOps` trait, providing `tap_ready`, 17 | `tap_not_ready` & `tap_err` (requires the *futures* crate). 18 | 19 | Futures do not provide mutable access for tap closures. 20 | 21 | * `nom3` - Exposes the `TapNomOps` trait, which provides `tap_done`, 22 | `tap_error`, and `tap_incomplete` on their respective variants of 23 | `nom::IResult`. 24 | 25 | ## Example 26 | 27 | ```rust 28 | extern crate tap; 29 | 30 | use tap::*; 31 | 32 | fn filter_map() { 33 | let values: &[Result] = &[Ok(3), Err("foo"), Err("bar"), Ok(8)]; 34 | 35 | let _ = values.iter().filter_map(|result| { 36 | // It is especially useful in filter maps, allowing error information to 37 | // be logged/printed before the information is discarded. 38 | result.tap_err(|error| eprintln!("Invalid entry: {}", error)).ok() 39 | }); 40 | } 41 | 42 | fn basic() { 43 | let mut foo = 5; 44 | 45 | // The `tap` extension can be used on all types 46 | if 10.tap(|v| foo += *v) > 0 { 47 | assert_eq!(foo, 15); 48 | } 49 | 50 | // Results have `tap_err` & `tap_ok` available. 51 | let _: Result = Err(5).tap_err(|e| foo = *e); 52 | assert_eq!(foo, 5); 53 | 54 | // Options have `tap_some` & `tap_none` available. 55 | let _: Option = None.tap_none(|| foo = 10); 56 | assert_eq!(foo, 10); 57 | } 58 | 59 | fn mutable() { 60 | let base = [1, 2, 3]; 61 | let mutated = base.tap(|arr| for elt in arr.iter_mut() { 62 | *elt *= 2; 63 | }); 64 | // base was consumed and is out of scope. 65 | assert_eq!(mutated, [2, 4, 6]); 66 | } 67 | ``` 68 | 69 | 70 | [crate-shield]: https://img.shields.io/crates/v/tap.svg?style=flat-square 71 | [crate]: https://crates.io/crates/tap 72 | [rust-shield]: https://img.shields.io/badge/powered%20by-rust-blue.svg?style=flat-square 73 | [rust]: https://www.rust-lang.org 74 | [docs-shield]: https://img.shields.io/badge/docs-latest-green.svg?style=flat-square 75 | [docs]: https://docs.rs/tap 76 | -------------------------------------------------------------------------------- /src/nom.rs: -------------------------------------------------------------------------------- 1 | //! Tap operations for nom's `IResult`, requires the feature `nom3`. 2 | extern crate nom; 3 | 4 | use self::nom::Err; 5 | use self::nom::IResult; 6 | use self::nom::Needed; 7 | 8 | /// Tap operations for nom's `IResult`, requires the feature `nom3`. 9 | pub trait TapNomOps { 10 | /// Executes a closure if the value is `IResult::Done`. 11 | /// 12 | /// The closure will receive a tuple of (unparsed input, parsed output). 13 | fn tap_done R>(self, f: F) -> Self; 14 | 15 | /// Executes a closure if the value is `IResult::Error`. 16 | fn tap_error) -> R>(self, f: F) -> Self; 17 | 18 | /// Executes a closure if the value is `IResult::Incomplete`. 19 | fn tap_incomplete R>(self, f: F) -> Self; 20 | } 21 | 22 | impl TapNomOps for IResult { 23 | fn tap_done R>(mut self, f: F) -> Self { 24 | use self::nom::IResult::Done; 25 | if let &mut Done(ref mut rem, ref mut val) = &mut self { 26 | let _ = f((rem, val)); 27 | } 28 | self 29 | } 30 | fn tap_error) -> R>(mut self, f: F) -> Self { 31 | use self::nom::IResult::Error; 32 | if let &mut Error(ref mut err) = &mut self { 33 | let _ = f(&mut *err); 34 | } 35 | self 36 | } 37 | fn tap_incomplete R>(mut self, f: F) -> Self { 38 | use self::nom::IResult::Incomplete; 39 | if let &mut Incomplete(ref mut needed) = &mut self { 40 | let _ = f(needed); 41 | } 42 | self 43 | } 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::nom::ErrorKind; 49 | use super::*; 50 | type TestResult = IResult<&'static str, i32, u32>; 51 | 52 | #[test] 53 | fn done() { 54 | let d: TestResult = IResult::Done(" 24", 42); 55 | let mut n = 0; 56 | d.tap_done(|(rem, val)| { 57 | assert_eq!(val, &mut 42); 58 | if let Ok(p) = rem.trim().parse::() { 59 | n = p; 60 | } 61 | }); 62 | assert_eq!(n, 24); 63 | } 64 | 65 | #[test] 66 | fn error() { 67 | let e: TestResult = IResult::Error(ErrorKind::Custom('t' as u32)); 68 | let mut err_code = 0; 69 | e.tap_error(|e| { 70 | if let ErrorKind::Custom(c) = *e { 71 | err_code = c; 72 | } 73 | }); 74 | assert_eq!(err_code, 116); 75 | } 76 | 77 | #[test] 78 | #[should_panic] 79 | fn incomplete() { 80 | let i: TestResult = IResult::Incomplete(Needed::Unknown); 81 | let mut more = 0; 82 | i.tap_incomplete(|i| { 83 | if let Needed::Size(s) = *i { 84 | more = s; 85 | } else { 86 | panic!(); 87 | } 88 | }); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A simple crate exposing tapping functionality for all types, and extended functionality for `Option`, `Result` & `Future`. 2 | //! 3 | //! The tap operation takes, and then returns, full ownership of the variable being tapped. 4 | //! This means that the closure may have mutable access to the variable, even if the variable is otherwise immutable. 5 | //! 6 | //! # Examples 7 | //! 8 | //! Logging error values: 9 | //! 10 | //! ```rust 11 | //! # use tap::*; 12 | //! let values: [Result; 4] = [Ok(3), Err("foo"), Err("bar"), Ok(8)]; 13 | //! 14 | //! let _ = values.iter().filter_map(|result| 15 | //! // print error information before discarding them 16 | //! result.tap_err(|error| eprintln!("Invalid entry: {}", error)).ok() 17 | //! ); 18 | //! ``` 19 | //! 20 | //! Chaining methods: 21 | //! 22 | //! ```rust 23 | //! # use tap::*; 24 | //! fn get_numbers() -> Vec { 25 | //! vec![4, 9, 1, 17, 3] 26 | //! } 27 | //! 28 | //! let mut old = get_numbers(); 29 | //! old.sort(); 30 | //! 31 | //! // can now be written like this instead 32 | //! let new = get_numbers().tap(|data| data.sort()); 33 | //! 34 | //! assert_eq!(old, new) 35 | //! ``` 36 | //! 37 | //! Reducing the amount of mutable variables: 38 | //! 39 | //! ```rust 40 | //! # use tap::*; 41 | //! let tapped = [1, 2, 3]; // does not need to be mutable, preventing accidental mutations 42 | //! let tapped = tapped.tap(|arr| { 43 | //! for elt in arr.iter_mut() { 44 | //! *elt *= 2; 45 | //! } 46 | //! }); 47 | //! 48 | //! // instead of 49 | //! let mut untapped = [1, 2, 3]; 50 | //! for elt in untapped.iter_mut() { 51 | //! *elt *= 2; 52 | //! } 53 | //! assert_eq!(tapped, untapped); 54 | //! ``` 55 | 56 | #[cfg(feature = "future")] 57 | pub use self::future::TapFutureOps; 58 | 59 | #[cfg(feature = "future")] 60 | mod future; 61 | 62 | #[cfg(feature = "nom3")] 63 | pub use self::nom::TapNomOps; 64 | 65 | #[cfg(feature = "nom3")] 66 | mod nom; 67 | 68 | /// Tap operations for `bool`. 69 | pub trait TapBooleanOps { 70 | /// Executes a closure if `self` is `true`. 71 | /// 72 | /// # Examples 73 | /// 74 | /// ```rust 75 | /// # use tap::*; 76 | /// let mut foo = 0; 77 | /// let boolean = false; 78 | /// assert_eq!(boolean.tap_true(|| foo += 5), false); 79 | /// assert_eq!(foo, 0); 80 | /// ``` 81 | /// 82 | /// ```rust 83 | /// # use tap::*; 84 | /// let mut foo = 0; 85 | /// let boolean = true; 86 | /// assert_eq!(boolean.tap_true(|| foo += 5), true); 87 | /// assert_eq!(foo, 5); 88 | /// ``` 89 | fn tap_true R>(self, f: F) -> Self; 90 | 91 | /// Executes a closure if `self` is `false`. 92 | /// 93 | /// # Examples 94 | /// 95 | /// ```rust 96 | /// # use tap::*; 97 | /// let mut foo = 0; 98 | /// let boolean = false; 99 | /// assert_eq!(boolean.tap_false(|| foo += 5), false); 100 | /// assert_eq!(foo, 5); 101 | /// ``` 102 | /// 103 | /// ```rust 104 | /// # use tap::*; 105 | /// let mut foo = 0; 106 | /// let boolean = true; 107 | /// assert_eq!(boolean.tap_false(|| foo += 5), true); 108 | /// assert_eq!(foo, 0); 109 | /// ``` 110 | fn tap_false R>(self, f: F) -> Self; 111 | } 112 | 113 | impl TapBooleanOps for bool { 114 | fn tap_true R>(self, f: F) -> Self { 115 | if self { 116 | let _ = f(); 117 | } 118 | self 119 | } 120 | 121 | fn tap_false R>(self, f: F) -> Self { 122 | if !self { 123 | let _ = f(); 124 | } 125 | self 126 | } 127 | } 128 | 129 | /// Tap operations for `Result`. 130 | pub trait TapResultOps { 131 | /// Executes a closure if the value is `Result::Ok(T)`. 132 | /// 133 | /// # Examples 134 | /// 135 | /// ```rust 136 | /// # use tap::*; 137 | /// let mut foo = 0; 138 | /// let res: Result = Ok(4); 139 | /// assert_eq!(res.tap_ok(|&mut v| foo += v), Ok(4)); 140 | /// assert_eq!(foo, 4); 141 | /// ``` 142 | /// 143 | /// ```rust 144 | /// # use tap::*; 145 | /// let mut foo = 0; 146 | /// let res: Result = Err(4); 147 | /// assert_eq!(res.tap_ok(|&mut v| foo += v), Err(4)); 148 | /// assert_eq!(foo, 0); 149 | /// ``` 150 | fn tap_ok R>(self, f: F) -> Self; 151 | 152 | /// Executes a closure if the value is `Result::Err(E)`. 153 | /// 154 | /// # Examples 155 | /// 156 | /// ```rust 157 | /// # use tap::*; 158 | /// let mut foo = 0; 159 | /// let res: Result = Ok(4); 160 | /// assert_eq!(res.tap_err(|&mut v| foo += v), Ok(4)); 161 | /// assert_eq!(foo, 0); 162 | /// ``` 163 | /// 164 | /// ```rust 165 | /// # use tap::*; 166 | /// let mut foo = 0; 167 | /// let res: Result = Err(4); 168 | /// assert_eq!(res.tap_err(|&mut v| foo += v), Err(4)); 169 | /// assert_eq!(foo, 4); 170 | /// ``` 171 | fn tap_err R>(self, f: F) -> Self; 172 | } 173 | 174 | impl TapResultOps for Result { 175 | fn tap_ok R>(mut self, f: F) -> Self { 176 | if let Ok(mut val) = self.as_mut() { 177 | let _ = f(&mut val); 178 | } 179 | self 180 | } 181 | 182 | fn tap_err R>(mut self, f: F) -> Self { 183 | if let Err(mut val) = self.as_mut() { 184 | let _ = f(&mut val); 185 | } 186 | self 187 | } 188 | } 189 | 190 | /// Tap operations for `Option`. 191 | pub trait TapOptionOps { 192 | /// Executes a closure if the value is `Option::Some(T)`. 193 | /// 194 | /// # Examples 195 | /// 196 | /// ```rust 197 | /// # use tap::*; 198 | /// let mut foo = 0; 199 | /// let res: Option = Some(4); 200 | /// assert_eq!(res.tap_some(|&mut v| foo += v), Some(4)); 201 | /// assert_eq!(foo, 4); 202 | /// ``` 203 | /// 204 | /// ```rust 205 | /// # use tap::*; 206 | /// let mut foo = 0; 207 | /// let res: Option = None; 208 | /// assert_eq!(res.tap_some(|&mut v| foo += v), None); 209 | /// assert_eq!(foo, 0); 210 | /// ``` 211 | fn tap_some R>(self, f: F) -> Self; 212 | 213 | /// Executes a closure if the value is `Option::None`. 214 | /// 215 | /// # Examples 216 | /// 217 | /// ```rust 218 | /// # use tap::*; 219 | /// let mut foo = 0; 220 | /// let res: Option = Some(4); 221 | /// assert_eq!(res.tap_none(|| foo += 5), Some(4)); 222 | /// assert_eq!(foo, 0); 223 | /// ``` 224 | /// 225 | /// ```rust 226 | /// # use tap::*; 227 | /// let mut foo = 0; 228 | /// let res: Option = None; 229 | /// assert_eq!(res.tap_none(|| foo += 5), None); 230 | /// assert_eq!(foo, 5); 231 | /// ``` 232 | fn tap_none R>(self, f: F) -> Self; 233 | } 234 | 235 | impl TapOptionOps for Option { 236 | fn tap_some R>(mut self, f: F) -> Self { 237 | if let Some(val) = self.as_mut() { 238 | let _ = f(val); 239 | } 240 | self 241 | } 242 | 243 | fn tap_none R>(self, f: F) -> Self { 244 | if self.is_none() { 245 | let _ = f(); 246 | } 247 | self 248 | } 249 | } 250 | 251 | /// Tap operations for all types. 252 | pub trait TapOps: Sized { 253 | /// Executes a closure on an object, discarding the result. 254 | /// 255 | /// # Examples 256 | /// 257 | /// ```rust 258 | /// # use tap::*; 259 | /// let mut max = 0; 260 | /// let data: [u32; 5] = [2, 8, 3, 4, 0]; 261 | /// assert_eq!( 262 | /// data.tap(|x| x.sort()).tap(|x| max += x.last().unwrap()), 263 | /// [0, 2, 3, 4, 8] 264 | /// ); 265 | /// assert_eq!(max, 8); 266 | /// ``` 267 | fn tap(self, f: F) -> Self 268 | where F: FnOnce(&mut Self) -> R; 269 | } 270 | 271 | impl TapOps for T where T: Sized { 272 | fn tap(mut self, f: F) -> Self 273 | where F: FnOnce(&mut Self) -> R 274 | { 275 | let _ = f(&mut self); 276 | self 277 | } 278 | } --------------------------------------------------------------------------------