apply_conditionally/
lib.rs

1//! # Apply Conditionally
2//!
3//! *Chain and apply methods on objects conditionally.*
4
5#![no_std]
6
7use either::IntoEither;
8
9/// Provides methods for conditionally applying methods on a type `Self`,
10/// whose size is constant and known at compile-time.
11///
12/// The [`ApplyConditionally::apply`] method always applies `f` on `self`.
13///
14/// The [`ApplyConditionally::apply_if`] method takes a [`bool`] to determine
15/// whether to apply `f` on `self` or not.
16///
17/// The [`ApplyConditionally::apply_or_else`] method takes a [`bool`] to
18/// determine whether to apply `f` or `g` on `self`.
19pub trait ApplyConditionally: Sized {
20    /// Applies `f` on `self`.
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use apply_conditionally::ApplyConditionally;
26    ///
27    /// fn add2(x: u32) -> u32 {
28    ///     x + 2
29    /// }
30    ///
31    /// fn mul2(x: u32) -> u32 {
32    ///     x * 2
33    /// }
34    ///
35    /// let x = 2;
36    /// assert_eq!(x.apply(add2).apply(mul2), 8);
37    /// ```
38    ///
39    /// is the same as doing:
40    ///
41    /// ```
42    /// use apply_conditionally::ApplyConditionally;
43    ///
44    /// fn add2(x: u32) -> u32 {
45    ///     x + 2
46    /// }
47    ///
48    /// fn mul2(x: u32) -> u32 {
49    ///     x * 2
50    /// }
51    ///
52    /// let x = 2;
53    /// assert_eq!(mul2(add2(x)), 8);
54    /// ```
55    #[inline]
56    fn apply<T, F>(self, f: F) -> T
57    where
58        F: FnOnce(Self) -> T,
59    {
60        f(self)
61    }
62
63    /// Applies `f` on `self` if and only if `condition` is `true`.
64    /// Does nothing otherwise.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use apply_conditionally::ApplyConditionally;
70    ///
71    /// fn u8_to_bools(value: u8, rtl: bool) -> [bool; 8] {
72    ///     let mut result = [false; 8];
73    ///     result
74    ///         .iter_mut()
75    ///         .apply_if(rtl, Iterator::rev)
76    ///         .enumerate()
77    ///         .for_each(|(i, b)| *b = ((value >> i) & 1) == 1);
78    ///
79    ///     result
80    /// }
81    ///
82    /// assert_eq!(
83    ///     u8_to_bools(0b00101101, false),
84    ///     [true, false, true, true, false, true, false, false]
85    /// );
86    /// assert_eq!(
87    ///     u8_to_bools(0b00101101, true),
88    ///     [false, false, true, false, true, true, false, true]
89    /// );
90    /// ```
91    ///
92    /// is the same as doing:
93    ///
94    /// ```
95    /// fn u8_to_bools(value: u8, rtl: bool) -> [bool; 8] {
96    ///     let mut result = [false; 8];
97    ///     if rtl {
98    ///         result
99    ///             .iter_mut()
100    ///             .rev()
101    ///             .enumerate()
102    ///             .for_each(|(i, b)| *b = ((value >> i) & 1) == 1);
103    ///     } else {
104    ///         result
105    ///             .iter_mut()
106    ///             .enumerate()
107    ///             .for_each(|(i, b)| *b = ((value >> i) & 1) == 1);
108    ///     }
109    ///
110    ///     result
111    /// }
112    ///
113    /// assert_eq!(
114    ///     u8_to_bools(0b00101101, false),
115    ///     [true, false, true, true, false, true, false, false]
116    /// );
117    /// assert_eq!(
118    ///     u8_to_bools(0b00101101, true),
119    ///     [false, false, true, false, true, true, false, true]
120    /// );
121    /// ```
122    #[inline]
123    fn apply_if<T, F>(self, condition: bool, f: F) -> either::Either<T, Self>
124    where
125        F: FnOnce(Self) -> T,
126    {
127        self.into_either(condition).map_left(f)
128    }
129
130    /// Applies `f` on `self` if `condition` is `true`.
131    /// Applies `g` otherwise.
132    ///
133    /// # Examples
134    ///
135    /// ```
136    /// use apply_conditionally::ApplyConditionally;
137    ///
138    /// fn get_pairs(bytes: &[u8], overlap: bool) -> Vec<(u8, u8)> {
139    ///     bytes
140    ///         .apply_or_else(overlap, |bytes| bytes.windows(2), |bytes| bytes.chunks(2))
141    ///         .map(|w| (w[0], w[1]))
142    ///         .collect()
143    /// }
144    ///
145    /// assert_eq!(get_pairs(b"abcd", false), vec![(b'a', b'b'), (b'c', b'd')]);
146    /// assert_eq!(
147    ///     get_pairs(b"abcd", true),
148    ///     vec![(b'a', b'b'), (b'b', b'c'), (b'c', b'd')]
149    /// );
150    /// ```
151    ///
152    /// is the same as doing:
153    ///
154    /// ```
155    /// fn get_pairs(bytes: &[u8], overlap: bool) -> Vec<(u8, u8)> {
156    ///     if overlap {
157    ///         bytes.windows(2).map(|w| (w[0], w[1])).collect()
158    ///     } else {
159    ///         bytes.chunks(2).map(|w| (w[0], w[1])).collect()
160    ///     }
161    /// }
162    ///
163    /// assert_eq!(get_pairs(b"abcd", false), vec![(b'a', b'b'), (b'c', b'd')]);
164    /// assert_eq!(
165    ///     get_pairs(b"abcd", true),
166    ///     vec![(b'a', b'b'), (b'b', b'c'), (b'c', b'd')]
167    /// );
168    /// ```
169    #[inline]
170    fn apply_or_else<L, R, F, G>(self, condition: bool, f: F, g: G) -> either::Either<L, R>
171    where
172        F: FnOnce(Self) -> L,
173        G: FnOnce(Self) -> R,
174    {
175        self.into_either(condition).map_either(f, g)
176    }
177}
178
179impl<T> ApplyConditionally for T {}
180
181#[cfg(test)]
182mod tests {
183    use super::*;
184
185    fn add2(x: usize) -> usize {
186        x + 2
187    }
188
189    fn mul2(x: usize) -> usize {
190        x * 2
191    }
192
193    #[test]
194    fn test_apply() {
195        let x = 2;
196
197        assert_eq!(x.apply(add2), add2(x));
198        assert_eq!(x.apply(add2).apply(|x| mul2(x)), mul2(add2(x)));
199    }
200
201    #[test]
202    fn test_apply_if() {
203        let x = 2;
204
205        assert_eq!(
206            x.apply_if(true, |x| x.apply(add2).apply(mul2)),
207            either::Either::Left(x.apply(add2).apply(mul2))
208        );
209        assert_eq!(
210            x.apply_if(false, |x| x.apply(add2).apply(mul2)),
211            either::Either::Right(x)
212        );
213    }
214
215    #[test]
216    fn test_apply_or_else() {
217        let x = 2;
218
219        assert_eq!(
220            x.apply_or_else(
221                true,
222                |x| x.apply(add2).apply(mul2),
223                |x| x.apply(mul2).apply(add2)
224            ),
225            either::Either::Left(x.apply(add2).apply(mul2))
226        );
227        assert_eq!(
228            x.apply_or_else(
229                false,
230                |x| x.apply(add2).apply(mul2),
231                |x| x.apply(mul2).apply(add2)
232            ),
233            either::Either::Right(x.apply(mul2).apply(add2))
234        );
235    }
236}