Skip to main content

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}