tuwunel_core/utils/option.rs
1//! Asynchronous mapping adapters for optional values.
2//!
3//! [`OptionExt`] turns an optional value into an optional future or a
4//! zero-or-one stream. The mapping closure runs only when the option contains a
5//! value.
6
7use futures::{FutureExt, Stream, future::OptionFuture};
8
9use super::IterStream;
10
11/// Extends [`Option`] with asynchronous mapping adapters.
12///
13/// [`OptionExt::map_async`] preserves optionality through future completion.
14/// [`OptionExt::map_stream`] exposes the mapped result as a zero-or-one stream.
15pub trait OptionExt<T> {
16 /// Maps the contained value to a future without eagerly awaiting it.
17 ///
18 /// A [`Some`] value yields an [`OptionFuture`] that resolves to the mapped
19 /// output. [`None`] resolves to `None` and never calls the mapping
20 /// closure.
21 fn map_async<F, Fut, U>(self, f: F) -> OptionFuture<Fut>
22 where
23 F: FnOnce(T) -> Fut,
24 Fut: Future<Output = U> + Send,
25 U: Send;
26
27 /// Maps the contained value to a future-backed stream with at most one
28 /// item.
29 ///
30 /// A [`Some`] value yields the mapped output after its future completes.
31 /// [`None`] yields an empty stream and never calls the mapping closure.
32 #[inline]
33 fn map_stream<F, Fut, U>(self, f: F) -> impl Stream<Item = U> + Send
34 where
35 F: FnOnce(T) -> Fut,
36 Fut: Future<Output = U> + Send,
37 U: Send,
38 Self: Sized,
39 {
40 self.map_async(f)
41 .map(Option::into_iter)
42 .map(IterStream::stream)
43 .flatten_stream()
44 }
45}
46
47impl<T> OptionExt<T> for Option<T> {
48 #[inline]
49 fn map_async<F, Fut, U>(self, f: F) -> OptionFuture<Fut>
50 where
51 F: FnOnce(T) -> Fut,
52 Fut: Future<Output = U> + Send,
53 U: Send,
54 {
55 self.map(f).into()
56 }
57}