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 std::marker::Unpin;
8
9use futures::{
10	TryFuture, TryFutureExt, future,
11	future::{MapOkOrElse, TrySelect, UnwrapOrElse},
12};
13
14/// This interface is not necessarily complete; feel free to add as-needed.
15pub trait TryExtExt<T, E>
16where
17	Self: TryFuture<Ok = T, Error = E> + Send,
18{
19	fn is_err(
20		self,
21	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
22	where
23		Self: Sized;
24
25	#[expect(clippy::wrong_self_convention)]
26	fn is_ok(
27		self,
28	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
29	where
30		Self: Sized;
31
32	fn map_ok_or<U, F>(
33		self,
34		default: U,
35		f: F,
36	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> U, impl FnOnce(Self::Error) -> U>
37	where
38		F: FnOnce(Self::Ok) -> U,
39		Self: Send + Sized;
40
41	fn ok(
42		self,
43	) -> MapOkOrElse<
44		Self,
45		impl FnOnce(Self::Ok) -> Option<Self::Ok>,
46		impl FnOnce(Self::Error) -> Option<Self::Ok>,
47	>
48	where
49		Self: Sized;
50
51	fn try_until<A, B, F>(self, f: F) -> TrySelect<A, B>
52	where
53		Self: Sized,
54		F: FnOnce() -> B,
55		A: TryFuture<Ok = Self::Ok> + From<Self> + Send + Unpin,
56		B: TryFuture<Ok = (), Error = Self::Error> + Send + Unpin;
57
58	fn unwrap_or(
59		self,
60		default: Self::Ok,
61	) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
62	where
63		Self: Sized;
64
65	fn unwrap_or_default(self) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
66	where
67		Self: Sized,
68		Self::Ok: Default;
69}
70
71impl<T, E, Fut> TryExtExt<T, E> for Fut
72where
73	Fut: TryFuture<Ok = T, Error = E> + Send,
74{
75	#[inline]
76	fn is_err(
77		self,
78	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
79	where
80		Self: Sized,
81	{
82		self.map_ok_or(true, |_| false)
83	}
84
85	#[inline]
86	fn is_ok(
87		self,
88	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> bool, impl FnOnce(Self::Error) -> bool>
89	where
90		Self: Sized,
91	{
92		self.map_ok_or(false, |_| true)
93	}
94
95	#[inline]
96	fn map_ok_or<U, F>(
97		self,
98		default: U,
99		f: F,
100	) -> MapOkOrElse<Self, impl FnOnce(Self::Ok) -> U, impl FnOnce(Self::Error) -> U>
101	where
102		F: FnOnce(Self::Ok) -> U,
103		Self: Send + Sized,
104	{
105		self.map_ok_or_else(|_| default, f)
106	}
107
108	#[inline]
109	fn ok(
110		self,
111	) -> MapOkOrElse<
112		Self,
113		impl FnOnce(Self::Ok) -> Option<Self::Ok>,
114		impl FnOnce(Self::Error) -> Option<Self::Ok>,
115	>
116	where
117		Self: Sized,
118	{
119		self.map_ok_or(None, Some)
120	}
121
122	#[inline]
123	fn try_until<A, B, F>(self, f: F) -> TrySelect<A, B>
124	where
125		Self: Sized,
126		F: FnOnce() -> B,
127		A: TryFuture<Ok = Self::Ok> + From<Self> + Send + Unpin,
128		B: TryFuture<Ok = (), Error = Self::Error> + Send + Unpin,
129	{
130		future::try_select(self.into(), f())
131	}
132
133	#[inline]
134	fn unwrap_or(
135		self,
136		default: Self::Ok,
137	) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
138	where
139		Self: Sized,
140	{
141		self.unwrap_or_else(move |_| default)
142	}
143
144	#[inline]
145	fn unwrap_or_default(self) -> UnwrapOrElse<Self, impl FnOnce(Self::Error) -> Self::Ok>
146	where
147		Self: Sized,
148		Self::Ok: Default,
149	{
150		self.unwrap_or(Default::default())
151	}
152}