devela/code/result/opt_res/
opt_res.rs

1// devela::code::result::option::optres
2//
3//!
4//
5// TOC
6// - type OptRes
7// - fns: sok, serr
8// - trait ExtOptRes
9
10#[doc = crate::TAG_RESULT!()]
11/// An optional result for simple ternary logic.
12///
13/// Combines [`Option`] and [`Result`] to handle three outcomes:
14/// success ([`Ok`]), failure ([`Err`]), or no value ([`None`]).
15///
16/// It can simplify insertion, removal, and value presence management for
17/// collections lacking [`Clone`] or [`Default`], by using [`Option::take`],
18/// and enhance control flow in stateful or asynchronous contexts.
19///
20/// See also: [`sok`] and [`serr`].
21///
22/// # Examples
23/// ```
24/// use devela::{sok, serr, OptRes};
25///
26/// #[derive(Debug, PartialEq)]
27/// struct V(i32);
28///
29/// fn process_results(results: &mut Vec<OptRes<V, &str>>) {
30///     println!("Processing...");
31///     let mut iter = results.iter_mut();
32///     while let Some(opt_res) = iter.next() {
33///         if let Some(res) = opt_res.take() {
34///             match res {
35///                 Ok(mut data) => {
36///                     println!("  Ok({})", data.0);
37///                     data.0 += 1; // modify the value
38///                     *opt_res = sok(data); // and put it back
39///                 }
40///                 Err(err) => {
41///                     println!("  Err({err})");
42///                     // leave the current None value
43///                 }
44///             }
45///         } else {
46///             println!("  None");
47///             *opt_res = serr("Beta"); // replace the None with an error
48///         }
49///     }
50/// }
51///
52/// let mut results: Vec<OptRes<V, &str>> = Vec::new();
53/// results.push(None);
54/// results.push(sok(V(10)));
55/// results.push(serr("Alpha"));
56/// results.push(sok(V(20)));
57///
58/// assert_eq![results, vec![None, sok(V(10)), serr("Alpha"), sok(V(20))]];
59/// process_results(&mut results);
60/// assert_eq![results, vec![serr("Beta"), sok(V(11)), None, sok(V(21))]];
61/// process_results(&mut results);
62/// assert_eq![results, vec![None, sok(V(12)), serr("Beta"), sok(V(22))]];
63/// ```
64///
65/// It should print:
66/// ```text
67/// Processing...
68///   None
69///   Ok(10)
70///   Err(Alpha)
71///   Ok(20)
72/// Processing...
73///   Err(Beta)
74///   Ok(11)
75///   None
76///   Ok(21)
77/// ```
78pub type OptRes<T, E> = Option<Result<T, E>>;
79
80/// Wraps the given [`OptRes`] `value` in a [`Some`]`(`[`Ok`]`(value))`.
81///
82/// See also: [`serr`].
83pub const fn sok<T, E>(value: T) -> OptRes<T, E> {
84    Some(Ok(value))
85}
86
87/// Wraps the given [`OptRes`] `value` in a [`Some`]`(`[`Err`]`(error))`.
88///
89/// See also: [`sok`].
90pub const fn serr<T, E>(error: E) -> OptRes<T, E> {
91    Some(Err(error))
92}
93
94/// Marker trait to prevent downstream implementations of the [`ExtOptRes`] trait.
95pub(super) trait Sealed {}
96impl<T, E> Sealed for OptRes<T, E> {}
97
98/// Extension trait providing additional methods for [`OptRes`].
99///
100/// This trait is sealed and cannot be implemented for any other type.
101///
102/// See also [`ExtOption`][crate::ExtOption],
103/// [`ExtResult`][crate::ExtResult],
104#[cfg_attr(feature = "nightly_doc", doc(notable_trait))]
105#[expect(private_bounds, reason = "Sealed")]
106pub trait ExtOptRes<T, E>: Sealed {
107    /// Transposes `Option<Result<T, E>>` into `Result<Option<T>, E>`.
108    ///
109    /// # Examples
110    /// ```
111    /// use devela::{ExtOptRes, OptRes};
112    ///
113    /// let a: OptRes<u8, &str> = None;
114    /// let b: OptRes<u8, &str> = Some(Ok(1));
115    /// let c: OptRes<u8, &str> = Some(Err("err"));
116    ///
117    /// assert_eq![a.transpose_result(), Ok(None)];
118    /// assert_eq![b.transpose_result(), Ok(Some(1))];
119    /// assert_eq![c.transpose_result(), Err("err")];
120    ///
121    /// // Comparison with std:
122    /// // a.transpose_result()
123    /// // match a { Some(Ok(t)) => Ok(Some(t)), Some(Err(e)) => Err(e), None => Ok(None) }
124    /// ```
125    fn transpose_result(self) -> Result<Option<T>, E>;
126
127    /// Unwraps the result if the `Option` is `Some`, otherwise calls the provided closure.
128    ///
129    /// # Examples
130    /// ```
131    /// use devela::{ExtOptRes, OptRes};
132    ///
133    /// let a: OptRes<u8, &str> = None;
134    /// let b: OptRes<u8, &str> = Some(Ok(1));
135    /// let c: OptRes<u8, &str> = Some(Err("err"));
136    ///
137    /// assert_eq![a.unwrap_or_else_result(|| Err("none")), Err("none")];
138    /// assert_eq![b.unwrap_or_else_result(|| Err("none")), Ok(1)];
139    /// assert_eq![c.unwrap_or_else_result(|| Err("none")), Err("err")];
140    ///
141    /// // Comparison with std:
142    /// // a.unwrap_or_else_result(|| Err("none"))
143    /// // a.unwrap_or_else(|| Err("none")).unwrap_or_else(|_| handle_err())
144    /// ```
145    fn unwrap_or_else_result<F: FnOnce() -> Result<T, E>>(self, f: F) -> Result<T, E>;
146
147    /// Applies a function to the `Ok` value inside `Option<Result<T, E>>`, if both are present.
148    ///
149    /// # Examples
150    /// ```
151    /// use devela::{ExtOptRes, OptRes};
152    ///
153    /// let a: OptRes<u8, &str> = None;
154    /// let b: OptRes<u8, &str> = Some(Ok(1));
155    /// let c: OptRes<u8, &str> = Some(Err("err"));
156    ///
157    /// assert_eq![a.map_ok(|v| v + 1), None];
158    /// assert_eq![b.map_ok(|v| v + 1), Some(Ok(2))];
159    /// assert_eq![c.map_ok(|v| v + 1), Some(Err("err"))];
160    ///
161    /// // Comparison with std:
162    /// // a.map_ok(|v| v + 1)
163    /// // a.map(|res| res.map(|v| v + 1))
164    /// ```
165    fn map_ok<U, F: FnOnce(T) -> U>(self, f: F) -> OptRes<U, E>;
166
167    /// Applies a function to the `Err` value inside `Option<Result<T, E>>`, if both are present.
168    ///
169    /// # Examples
170    /// ```
171    /// use devela::{ExtOptRes, OptRes};
172    ///
173    /// let a: OptRes<u8, &str> = None;
174    /// let b: OptRes<u8, &str> = Some(Ok(1));
175    /// let c: OptRes<u8, &str> = Some(Err("err"));
176    ///
177    /// assert_eq![a.map_err(|_e| "new_err"), None];
178    /// assert_eq![b.map_err(|_e| "new_err"), Some(Ok(1))];
179    /// assert_eq![c.map_err(|_e| "new_err"), Some(Err("new_err"))];
180    ///
181    /// // Comparison with std:
182    /// // a.map_err(|e| handle_error(e))
183    /// // a.map(|res| res.map_err(|e| handle_error(e)))
184    /// ```
185    fn map_err<F, G: FnOnce(E) -> F>(self, f: G) -> OptRes<T, F>;
186
187    /// Provides a default error if the `Option` is `None`.
188    ///
189    /// # Examples
190    /// ```
191    /// use devela::{ExtOptRes, OptRes};
192    ///
193    /// let a: OptRes<u8, &str> = None;
194    /// let b: OptRes<u8, &str> = Some(Ok(1));
195    /// let c: OptRes<u8, &str> = Some(Err("err"));
196    ///
197    /// assert_eq![a.ok_or_default_err(), Err("")];
198    /// assert_eq![b.ok_or_default_err(), Ok(1)];
199    /// assert_eq![c.ok_or_default_err(), Err("err")];
200    ///
201    /// // Comparison with std:
202    /// // a.ok_or_default_err()
203    /// // a.ok_or_else(|| Err(Default::default()))
204    /// ```
205    fn ok_or_default_err(self) -> Result<T, E>
206    where
207        E: Default;
208}
209
210impl<T, E> ExtOptRes<T, E> for OptRes<T, E> {
211    fn transpose_result(self) -> Result<Option<T>, E> {
212        match self {
213            Some(Ok(t)) => Ok(Some(t)),
214            Some(Err(e)) => Err(e),
215            None => Ok(None),
216        }
217    }
218
219    fn unwrap_or_else_result<F: FnOnce() -> Result<T, E>>(self, f: F) -> Result<T, E> {
220        match self {
221            Some(result) => result,
222            None => f(),
223        }
224    }
225
226    fn map_ok<U, F: FnOnce(T) -> U>(self, f: F) -> OptRes<U, E> {
227        self.map(|res| res.map(f))
228    }
229
230    fn map_err<F, G: FnOnce(E) -> F>(self, f: G) -> OptRes<T, F> {
231        self.map(|res| res.map_err(f))
232    }
233
234    fn ok_or_default_err(self) -> Result<T, E>
235    where
236        E: Default,
237    {
238        self.unwrap_or_else(|| Err(E::default()))
239    }
240}