Skip to main content

tuwunel_core/utils/future/
bool_ext.rs

1//! Extended external extensions to futures::FutureExt
2#![expect(clippy::many_single_char_names, clippy::impl_trait_in_params)]
3
4use futures::{
5	FutureExt,
6	future::{
7		Either::{Left, Right},
8		select_ok, try_join, try_join_all, try_join3, try_join4,
9	},
10};
11
12use crate::utils::BoolExt as _;
13
14/// Combines Boolean futures with concurrent short-circuit logic.
15///
16/// Conjunction resolves false on the first false output and true only after
17/// every input resolves true. Disjunction resolves true on the first true
18/// output and false only after every input resolves false.
19pub trait BoolExt
20where
21	Self: Future<Output = bool> + Send,
22{
23	/// Computes the disjunction of two Boolean futures.
24	///
25	/// Both futures are polled concurrently. The returned future resolves true
26	/// on the first true output or false after both produce false.
27	fn or<B>(self, b: B) -> impl Future<Output = bool> + Send
28	where
29		B: Future<Output = bool> + Send + Unpin,
30		Self: Sized + Unpin;
31
32	/// Computes the conjunction of two Boolean futures.
33	///
34	/// Both futures are polled concurrently. The returned future resolves false
35	/// on the first false output or true after both produce true.
36	fn and<B>(self, b: B) -> impl Future<Output = bool> + Send
37	where
38		B: Future<Output = bool> + Send,
39		Self: Sized;
40
41	/// Computes the conjunction of three Boolean futures.
42	///
43	/// The receiver and both arguments are polled concurrently. The result is
44	/// true only when all three futures produce true.
45	fn and2<B, C>(self, b: B, c: C) -> impl Future<Output = bool> + Send
46	where
47		B: Future<Output = bool> + Send,
48		C: Future<Output = bool> + Send,
49		Self: Sized;
50
51	/// Computes the conjunction of four Boolean futures.
52	///
53	/// The receiver and all three arguments are polled concurrently. The result
54	/// is true only when every future produces true.
55	fn and3<B, C, D>(self, b: B, c: C, d: D) -> impl Future<Output = bool> + Send
56	where
57		B: Future<Output = bool> + Send,
58		C: Future<Output = bool> + Send,
59		D: Future<Output = bool> + Send,
60		Self: Sized;
61}
62
63impl<Fut> BoolExt for Fut
64where
65	Fut: Future<Output = bool> + Send,
66{
67	fn or<B>(self, b: B) -> impl Future<Output = bool> + Send
68	where
69		B: Future<Output = bool> + Send + Unpin,
70		Self: Sized + Unpin,
71	{
72		select_ok([Left(self.map(test)), Right(b.map(test))]).map(|res| res.is_ok())
73	}
74
75	fn and<B>(self, b: B) -> impl Future<Output = bool> + Send
76	where
77		B: Future<Output = bool> + Send,
78		Self: Sized,
79	{
80		try_join(self.map(test), b.map(test)).map(|res| res.is_ok())
81	}
82
83	fn and2<B, C>(self, b: B, c: C) -> impl Future<Output = bool> + Send
84	where
85		B: Future<Output = bool> + Send,
86		C: Future<Output = bool> + Send,
87		Self: Sized,
88	{
89		try_join3(self.map(test), b.map(test), c.map(test)).map(|res| res.is_ok())
90	}
91
92	fn and3<B, C, D>(self, b: B, c: C, d: D) -> impl Future<Output = bool> + Send
93	where
94		B: Future<Output = bool> + Send,
95		C: Future<Output = bool> + Send,
96		D: Future<Output = bool> + Send,
97		Self: Sized,
98	{
99		try_join4(self.map(test), b.map(test), c.map(test), d.map(test)).map(|res| res.is_ok())
100	}
101}
102
103/// Computes the conjunction of an iterator of Boolean futures.
104///
105/// All inputs are polled concurrently and false short-circuits the operation.
106/// An empty iterator resolves to true.
107pub fn and<I, F>(args: I) -> impl Future<Output = bool> + Send
108where
109	I: Iterator<Item = F> + Send,
110	F: Future<Output = bool> + Send,
111{
112	let args = args.map(|a| a.map(test));
113
114	try_join_all(args).map(|res| res.is_ok())
115}
116
117/// Computes the disjunction of an iterator of Boolean futures.
118///
119/// All inputs are polled concurrently and true short-circuits the operation.
120/// False is returned only after every input resolves to false.
121///
122/// # Panics
123///
124/// Panics when the iterator contains no futures.
125pub fn or<I, F>(args: I) -> impl Future<Output = bool> + Send
126where
127	I: Iterator<Item = F> + Send,
128	F: Future<Output = bool> + Send + Unpin,
129{
130	let args = args.map(|a| a.map(test));
131
132	select_ok(args).map(|res| res.is_ok())
133}
134
135/// Computes the conjunction of four Boolean futures.
136///
137/// All four inputs are polled concurrently. The result is true only when every
138/// future resolves to true.
139pub fn and4(
140	a: impl Future<Output = bool> + Send,
141	b: impl Future<Output = bool> + Send,
142	c: impl Future<Output = bool> + Send,
143	d: impl Future<Output = bool> + Send,
144) -> impl Future<Output = bool> + Send {
145	a.and3(b, c, d)
146}
147
148/// Computes the conjunction of five Boolean futures.
149///
150/// All five inputs are polled concurrently. The result is true only when every
151/// future resolves to true.
152pub fn and5(
153	a: impl Future<Output = bool> + Send,
154	b: impl Future<Output = bool> + Send,
155	c: impl Future<Output = bool> + Send,
156	d: impl Future<Output = bool> + Send,
157	e: impl Future<Output = bool> + Send,
158) -> impl Future<Output = bool> + Send {
159	a.and2(b, c).and2(d, e)
160}
161
162/// Computes the conjunction of six Boolean futures.
163///
164/// All six inputs are polled concurrently. The result is true only when every
165/// future resolves to true.
166pub fn and6(
167	a: impl Future<Output = bool> + Send,
168	b: impl Future<Output = bool> + Send,
169	c: impl Future<Output = bool> + Send,
170	d: impl Future<Output = bool> + Send,
171	e: impl Future<Output = bool> + Send,
172	f: impl Future<Output = bool> + Send,
173) -> impl Future<Output = bool> + Send {
174	a.and3(b, c, d).and2(e, f)
175}
176
177/// Computes the conjunction of seven Boolean futures.
178///
179/// All seven inputs are polled concurrently. The result is true only when every
180/// future resolves to true.
181pub fn and7(
182	a: impl Future<Output = bool> + Send,
183	b: impl Future<Output = bool> + Send,
184	c: impl Future<Output = bool> + Send,
185	d: impl Future<Output = bool> + Send,
186	e: impl Future<Output = bool> + Send,
187	f: impl Future<Output = bool> + Send,
188	g: impl Future<Output = bool> + Send,
189) -> impl Future<Output = bool> + Send {
190	a.and3(b, c, d).and3(e, f, g)
191}
192
193fn test(test: bool) -> crate::Result<(), ()> { test.into_result() }