lifterr/
option.rs

1//! Extra utilities for handling optionality.
2//!
3//! This module extends capabilities offered by [`std::option`].
4
5/// Extension with a set of extra combinators for `Option<A>`.
6pub trait OptionExt<A> {
7    /// Applies `f` yielding yet another option if `Some(x)` otherwise propagates `None`.
8    ///
9    /// ```
10    /// use lifterr::option::OptionExt;
11    ///
12    /// fn some() -> Option<i32> { Some(1) }
13    /// fn none() -> Option<i32> { None }
14    ///
15    /// assert_eq!(some().then(|| Some("42")), Some("42"));
16    /// assert_eq!(none().then(|| Some("42")), None);
17    /// ```
18    fn then<F, B>(self, f: F) -> Option<B>
19    where
20        F: FnOnce() -> Option<B>;
21
22    /// Applies `f` yielding a value which is then wrapped into another option if `Some(x)` otherwise propagates `None`.
23    ///
24    /// ```
25    /// use lifterr::option::OptionExt;
26    ///
27    /// fn some() -> Option<i32> { Some(1) }
28    /// fn none() -> Option<i32> { None }
29    ///
30    /// assert_eq!(some().remap(|| "42"), Some("42"));
31    /// assert_eq!(none().remap(|| "42"), None);
32    /// ```
33    fn remap<F, B>(self, f: F) -> Option<B>
34    where
35        Self: Sized,
36        F: FnOnce() -> B,
37    {
38        self.then(|| f().into())
39    }
40
41    /// Replaces whatever value of type `A` in `Option<A>` with an unit.
42    fn void(self) -> Option<()>
43    where
44        Self: Sized,
45    {
46        self.remap(|| ())
47    }
48
49    /// Runs `f` with a reference to `A` when `Some(a)`.
50    ///
51    /// ```
52    /// use lifterr::option::OptionExt;
53    /// assert_eq!(Some(10).inspect(|a| println!("a = {a}")), Some(10));
54    /// ```
55    fn inspect<F>(self, f: F) -> Option<A>
56    where
57        F: FnOnce(&A);
58
59    /// Recovers from an absent value with a total function.
60    fn recover<F>(self, f: F) -> Option<A>
61    where
62        F: FnOnce() -> A,
63        Self: Sized,
64    {
65        self.recover_with(|| f().into())
66    }
67
68    /// Recovers from an absent value with a partial function.
69    ///
70    /// ```
71    /// use lifterr::option::OptionExt;
72    ///
73    /// fn not_found() -> Option<i32> { None }
74    /// fn fallback() -> Option<i32> { Some(42) }
75    ///
76    /// assert_eq!(Some(10).recover_with(fallback), Some(10));
77    /// assert_eq!(not_found().recover_with(fallback), Some(42));
78    /// ```
79    fn recover_with<F>(self, f: F) -> Option<A>
80    where
81        F: FnOnce() -> Option<A>;
82}
83
84impl<A> OptionExt<A> for Option<A> {
85    fn then<F, B>(self, f: F) -> Option<B>
86    where
87        F: FnOnce() -> Option<B>,
88    {
89        self.and_then(|_| f())
90    }
91
92    fn inspect<F>(self, f: F) -> Option<A>
93    where
94        F: FnOnce(&A),
95    {
96        self.map(|a| {
97            f(&a);
98            a
99        })
100    }
101
102    fn recover_with<F>(self, f: F) -> Option<A>
103    where
104        F: FnOnce() -> Option<A>,
105    {
106        self.map_or_else(f, A::into)
107    }
108}