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}