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}