Skip to main content

tuwunel_core/utils/future/
try_ext_ext.rs

1//! Extended external extensions to futures::TryFutureExt
2#![expect(clippy::type_complexity)]
3// is_ok() has to consume *self rather than borrow. This extension is for a
4// caller only ever caring about result status while discarding all contents.
5#![expect(clippy::wrong_self_convention)]
6
7use futures::{
8	TryFuture, TryFutureExt, future,
9	future::{MapOkOrElse, TrySelect, UnwrapOrElse},
10};
11
12/// Adds result-like combinators to fallible futures.
13///
14/// The adapters transform the eventual success or error without awaiting the
15/// future early. Inspection helpers reduce either result branch to a Boolean
16/// and discard its payload.
17pub trait TryExtExt<T, E>
18where
19	Self: TryFuture<Ok = T, Error = E> + Send,
20{
21	/// Reports whether the future resolves to an error.
22	///
23	/// Both output payloads are discarded after the future completes. The
24	/// returned future preserves only the error-state Boolean.
25	fn is_err(
26		self,
27	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
28	where
29		Self: Sized;
30
31	/// Reports whether the future resolves successfully.
32	///
33	/// Both output payloads are discarded after the future completes. The
34	/// returned future preserves only the success-state Boolean.
35	#[expect(clippy::wrong_self_convention)]
36	fn is_ok(
37		self,
38	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
39	where
40		Self: Sized;
41
42	/// Maps a successful output or returns an eager fallback for errors.
43	///
44	/// The mapping closure runs only for the success branch. The original error
45	/// is discarded, and the fallback is dropped when mapping succeeds.
46	fn map_ok_or<U, F>(
47		self,
48		default: U,
49		f: F,
50	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> U, impl FnOnce(Self::Error) -> U>
51	where
52		F: FnOnce(Self::Ok) -> U,
53		Self: Send + Sized;
54
55	/// Converts the fallible future's output into an option.
56	///
57	/// A successful value becomes `Some`, while any error becomes `None`. The
58	/// original error value is discarded.
59	fn ok(
60		self,
61	) -> MapOkOrElse<
62		Self,
63		impl FnOnce(Self::Ok) -> Option<Self::Ok>,
64		impl FnOnce(Self::Error) -> Option<Self::Ok>,
65	>
66	where
67		Self: Sized;
68
69	/// Races the receiver against a fallible unit-output stopping future.
70	///
71	/// `f` constructs the stopping future when the selector is created. The
72	/// returned [`TrySelect`] preserves the winning result and unfinished
73	/// future.
74	fn try_until<A, B, F>(self, f: F) -> TrySelect<A, B>
75	where
76		Self: Sized,
77		F: FnOnce() -> B,
78		A: TryFuture<Ok = Self::Ok> + From<Self> + Send + Unpin,
79		B: TryFuture<Ok = (), Error = Self::Error> + Send + Unpin;
80
81	/// Returns a successful output or an eager fallback for errors.
82	///
83	/// The original error is discarded after the future resolves. The fallback
84	/// is dropped when the future succeeds.
85	fn unwrap_or(
86		self,
87		default: Self::Ok,
88	) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
89	where
90		Self: Sized;
91
92	/// Returns a successful output or its type's default for errors.
93	///
94	/// The default is constructed eagerly when the adapter is created and is
95	/// dropped when the future succeeds. The original error value is discarded.
96	fn unwrap_or_default(self) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
97	where
98		Self: Sized,
99		Self::Ok: Default;
100}
101
102impl<T, E, Fut> TryExtExt<T, E> for Fut
103where
104	Fut: TryFuture<Ok = T, Error = E> + Send,
105{
106	#[inline]
107	fn is_err(
108		self,
109	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
110	where
111		Self: Sized,
112	{
113		self.map_ok_or(true, |_| false)
114	}
115
116	#[inline]
117	fn is_ok(
118		self,
119	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
120	where
121		Self: Sized,
122	{
123		self.map_ok_or(false, |_| true)
124	}
125
126	#[inline]
127	fn map_ok_or<U, F>(
128		self,
129		default: U,
130		f: F,
131	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> U, impl FnOnce(Self::Error) -> U>
132	where
133		F: FnOnce(Self::Ok) -> U,
134		Self: Send + Sized,
135	{
136		self.map_ok_or_else(|_| default, f)
137	}
138
139	#[inline]
140	fn ok(
141		self,
142	) -> MapOkOrElse<
143		Self,
144		impl FnOnce(Self::Ok) -> Option<Self::Ok>,
145		impl FnOnce(Self::Error) -> Option<Self::Ok>,
146	>
147	where
148		Self: Sized,
149	{
150		self.map_ok_or(None, Some)
151	}
152
153	#[inline]
154	fn try_until<A, B, F>(self, f: F) -> TrySelect<A, B>
155	where
156		Self: Sized,
157		F: FnOnce() -> B,
158		A: TryFuture<Ok = Self::Ok> + From<Self> + Send + Unpin,
159		B: TryFuture<Ok = (), Error = Self::Error> + Send + Unpin,
160	{
161		future::try_select(self.into(), f())
162	}
163
164	#[inline]
165	fn unwrap_or(
166		self,
167		default: Self::Ok,
168	) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
169	where
170		Self: Sized,
171	{
172		self.unwrap_or_else(move |_| default)
173	}
174
175	#[inline]
176	fn unwrap_or_default(self) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
177	where
178		Self: Sized,
179		Self::Ok: Default,
180	{
181		self.unwrap_or(Default::default())
182	}
183}