Skip to main content

fallible_map/
lib.rs

1/// `fallible_map_ext` provides utilities for fallible mapping over `Option`
2/// types and iterators, allowing the use of functions that can return `Result`s.
3
4/// A helper trait to extract the inner value of an optional container.
5pub trait ExtractOption<T> {
6    /// Extract the inner value as an `Option`.
7    fn extract(self) -> Option<T>;
8}
9
10/// Implementation of `ExtractOption` for `Option`.
11impl<T> ExtractOption<T> for Option<T> {
12    fn extract(self) -> Option<T> {
13        self
14    }
15}
16
17/// Extend `Option` with fallible methods.
18///
19/// Useful for mapping fallible operations (i.e., operations that return `Result`),
20/// over an optional type. The result will be `Result<Option<U>>`, making it easy
21/// to handle errors originating from inside the closure being mapped.
22///
23/// # Type Parameters
24///
25/// - `C`: The container type that implements `ExtractOption`
26/// - `T`: The input container's value type
27/// - `U`: The output container's value type
28/// - `E`: The possible error type during the mapping
29pub trait FallibleMapExt<T, E> {
30    /// Attempt to map a function over an optional value.
31    ///
32    /// # Parameters
33    ///
34    /// - `f`: A function that takes a value of type `T` and returns a `Result<U, E>`.
35    ///
36    /// # Returns
37    ///
38    /// A `Result` containing an `Option<U>`, or an error `E`.
39    fn try_map<F, U>(self, f: F) -> Result<Option<U>, E>
40    where
41        F: FnOnce(T) -> Result<U, E>;
42
43    /// Unwrap an optional value or compute a fallback.
44    ///
45    /// # Parameters
46    ///
47    /// - `f`: A function that returns a `Result<T, E>`.
48    ///
49    /// # Returns
50    ///
51    /// A `Result` containing a value of type `T`, or an error `E`.
52    fn try_unwrap_or<F>(self, f: F) -> Result<T, E>
53    where
54        F: FnOnce() -> Result<T, E>;
55
56    /// Chain computation that returns another optional value.
57    ///
58    /// # Parameters
59    ///
60    /// - `f`: A function that takes a value of type `T` and returns a `Result<Option<U>, E>`.
61    ///
62    /// # Returns
63    ///
64    /// A `Result` containing an `Option<U>`, or an error `E`.
65    fn try_and_then<F, U>(self, f: F) -> Result<Option<U>, E>
66    where
67        F: FnOnce(T) -> Result<Option<U>, E>;
68}
69
70/// Implementation of `FallibleMapExt` for types implementing `ExtractOption`.
71impl<C, T, E> FallibleMapExt<T, E> for C
72where
73    C: ExtractOption<T>,
74{
75    fn try_map<F, U>(self, f: F) -> Result<Option<U>, E>
76    where
77        F: FnOnce(T) -> Result<U, E>,
78    {
79        match self.extract() {
80            Some(x) => f(x).map(Some),
81            None => Ok(None),
82        }
83    }
84
85    fn try_unwrap_or<F>(self, f: F) -> Result<T, E>
86    where
87        F: FnOnce() -> Result<T, E>,
88    {
89        match self.extract() {
90            Some(x) => Ok(x),
91            None => f(),
92        }
93    }
94
95    fn try_and_then<F, U>(self, f: F) -> Result<Option<U>, E>
96    where
97        F: FnOnce(T) -> Result<Option<U>, E>,
98    {
99        match self.extract() {
100            Some(x) => f(x),
101            None => Ok(None),
102        }
103    }
104}
105
106/// A fallible map iterator that maps a function returning a `Result` over the elements of the underlying iterator.
107pub struct FallibleMapIterator<I, F, B, E> {
108    iter: I,
109    f: F,
110    _marker: std::marker::PhantomData<(B, E)>,
111}
112
113impl<I, F, B, E> FallibleMapIterator<I, F, B, E> {
114    pub fn new(iter: I, f: F) -> Self {
115        FallibleMapIterator {
116            iter,
117            f,
118            _marker: std::marker::PhantomData,
119        }
120    }
121}
122
123/// Implement `Iterator` for `FallibleMap` where the iterator item is a `Result`.
124impl<I, F, B, E> Iterator for FallibleMapIterator<I, F, B, E>
125where
126    I: Iterator,
127    F: FnMut(I::Item) -> Result<B, E>,
128{
129    type Item = Result<B, E>;
130
131    fn next(&mut self) -> Option<Self::Item> {
132        self.iter.next().map(&mut self.f)
133    }
134}
135
136/// Extend iterator with fallible map functionality.
137pub trait FallibleMapIteratorExt: Iterator {
138    /// Attempt to map a function over an iterator, returning a `Result` iterator.
139    ///
140    /// # Parameters
141    ///
142    /// - `f`: A function that takes an item and returns a `Result<B, E>`.
143    ///
144    /// # Returns
145    ///
146    /// An iterator where each item is a `Result<B, E>`.
147    fn try_map<B, F, E>(self, f: F) -> FallibleMapIterator<Self, F, B, E>
148    where
149        Self: Sized,
150        F: FnMut(Self::Item) -> Result<B, E>;
151}
152
153/// Implementation of `FallibleMapIteratorExt` for all iterators.
154impl<I> FallibleMapIteratorExt for I
155where
156    I: Iterator,
157{
158    fn try_map<B, F, E>(self, f: F) -> FallibleMapIterator<Self, F, B, E>
159    where
160        Self: Sized,
161        F: FnMut(Self::Item) -> Result<B, E>,
162    {
163        FallibleMapIterator::new(self, f)
164    }
165}