Skip to main content

tuwunel_core/utils/stream/
ready.rs

1//! Synchronous combinator extensions to futures::Stream
2#![expect(clippy::type_complexity)]
3
4use futures::{
5	future::{FutureExt, Ready, ready},
6	stream::{
7		All, Any, Filter, FilterMap, Fold, ForEach, Scan, SkipWhile, Stream, StreamExt, TakeWhile,
8	},
9};
10
11/// Adds synchronous counterparts to selected [`StreamExt`] combinators.
12///
13/// These methods accept ordinary closures for operations that do not need to
14/// await, avoiding an async block around iterator-like predicates and folds.
15pub trait ReadyExt<Item>
16where
17	Self: Stream<Item = Item> + Sized,
18{
19	/// Tests whether every item satisfies a synchronous predicate.
20	///
21	/// Evaluation stops at the first false result. An empty stream resolves to
22	/// true.
23	fn ready_all<F>(self, f: F) -> All<Self, Ready<bool>, impl FnMut(Item) -> Ready<bool>>
24	where
25		F: Fn(Item) -> bool;
26
27	/// Tests whether any item satisfies a synchronous predicate.
28	///
29	/// Evaluation stops at the first true result. An empty stream resolves to
30	/// false.
31	fn ready_any<F>(self, f: F) -> Any<Self, Ready<bool>, impl FnMut(Item) -> Ready<bool>>
32	where
33		F: Fn(Item) -> bool;
34
35	/// Finds the first item satisfying a synchronous predicate.
36	///
37	/// Items are tested in source order and the first match is returned. The
38	/// future resolves to `None` when the stream ends without a match.
39	fn ready_find<'a, F>(self, f: F) -> impl Future<Output = Option<Item>> + Send
40	where
41		Self: Send + Unpin + 'a,
42		F: Fn(&Item) -> bool + Send + 'a,
43		Item: Send;
44
45	/// Finds the first value produced by a synchronous mapping predicate.
46	///
47	/// Items are mapped in source order until `f` returns `Some`. The future
48	/// resolves to `None` when every item maps to absence.
49	fn ready_find_map<'a, F, U>(self, f: F) -> impl Future<Output = Option<U>> + Send
50	where
51		Self: Send + Unpin + 'a,
52		F: Fn(Item) -> Option<U> + Send + 'a,
53		Item: Send,
54		U: Send;
55
56	/// Retains items accepted by a synchronous predicate.
57	///
58	/// The predicate borrows each item as it is polled. Accepted items preserve
59	/// their source order and value.
60	fn ready_filter<'a, F>(
61		self,
62		f: F,
63	) -> Filter<Self, Ready<bool>, impl FnMut(&Item) -> Ready<bool> + 'a>
64	where
65		F: Fn(&Item) -> bool + 'a;
66
67	/// Maps items synchronously while filtering absent results.
68	///
69	/// Values returned in `Some` are yielded in source order. Items mapped to
70	/// `None` are discarded.
71	fn ready_filter_map<F, U>(
72		self,
73		f: F,
74	) -> FilterMap<Self, Ready<Option<U>>, impl FnMut(Item) -> Ready<Option<U>>>
75	where
76		F: Fn(Item) -> Option<U>;
77
78	/// Folds items synchronously from an explicit initial accumulator.
79	///
80	/// `f` receives the accumulator and each item in source order. The future
81	/// resolves to the final accumulator after the stream ends.
82	fn ready_fold<T, F>(
83		self,
84		init: T,
85		f: F,
86	) -> Fold<Self, Ready<T>, T, impl FnMut(T, Item) -> Ready<T>>
87	where
88		F: Fn(T, Item) -> T;
89
90	/// Folds items synchronously from `T::default()`.
91	///
92	/// `f` receives the accumulator and each item in source order. An empty
93	/// stream resolves to the default accumulator unchanged.
94	fn ready_fold_default<T, F>(
95		self,
96		f: F,
97	) -> Fold<Self, Ready<T>, T, impl FnMut(T, Item) -> Ready<T>>
98	where
99		F: Fn(T, Item) -> T,
100		T: Default;
101
102	/// Applies a synchronous closure to every stream item.
103	///
104	/// Items are consumed in source order. The returned future resolves after
105	/// the stream ends and carries no output value.
106	fn ready_for_each<F>(self, f: F) -> ForEach<Self, Ready<()>, impl FnMut(Item) -> Ready<()>>
107	where
108		F: FnMut(Item);
109
110	/// Yields the leading items accepted by a synchronous predicate.
111	///
112	/// Evaluation stops at the first false result, and that boundary item is
113	/// not yielded. Accepted items retain source order.
114	fn ready_take_while<'a, F>(
115		self,
116		f: F,
117	) -> TakeWhile<Self, Ready<bool>, impl FnMut(&Item) -> Ready<bool> + 'a>
118	where
119		F: Fn(&Item) -> bool + 'a;
120
121	/// Transforms items with mutable state and a synchronous scan closure.
122	///
123	/// Each `Some` result is yielded while `None` terminates the stream. State
124	/// is initialized once and retained between items.
125	fn ready_scan<B, T, F>(
126		self,
127		init: T,
128		f: F,
129	) -> Scan<Self, T, Ready<Option<B>>, impl FnMut(&mut T, Item) -> Ready<Option<B>>>
130	where
131		F: Fn(&mut T, Item) -> Option<B>;
132
133	/// Observes each item with mutable state and yields it unchanged.
134	///
135	/// The closure receives the persistent state and a shared reference to each
136	/// item. Every source item is then emitted in order.
137	fn ready_scan_each<T, F>(
138		self,
139		init: T,
140		f: F,
141	) -> Scan<Self, T, Ready<Option<Item>>, impl FnMut(&mut T, Item) -> Ready<Option<Item>>>
142	where
143		F: Fn(&mut T, &Item);
144
145	/// Skips the leading items accepted by a synchronous predicate.
146	///
147	/// The first false item and every later item are yielded in source order.
148	/// The predicate is no longer called after the first false result.
149	fn ready_skip_while<'a, F>(
150		self,
151		f: F,
152	) -> SkipWhile<Self, Ready<bool>, impl FnMut(&Item) -> Ready<bool> + 'a>
153	where
154		F: Fn(&Item) -> bool + 'a;
155}
156
157impl<Item, S> ReadyExt<Item> for S
158where
159	S: Stream<Item = Item> + Sized,
160{
161	#[inline]
162	fn ready_all<F>(self, f: F) -> All<Self, Ready<bool>, impl FnMut(Item) -> Ready<bool>>
163	where
164		F: Fn(Item) -> bool,
165	{
166		self.all(move |t| ready(f(t)))
167	}
168
169	#[inline]
170	fn ready_any<F>(self, f: F) -> Any<Self, Ready<bool>, impl FnMut(Item) -> Ready<bool>>
171	where
172		F: Fn(Item) -> bool,
173	{
174		self.any(move |t| ready(f(t)))
175	}
176
177	#[inline]
178	fn ready_find<'a, F>(self, f: F) -> impl Future<Output = Option<Item>> + Send
179	where
180		Self: Send + Unpin + 'a,
181		F: Fn(&Item) -> bool + Send + 'a,
182		Item: Send,
183	{
184		self.ready_filter(f)
185			.take(1)
186			.into_future()
187			.map(|(curr, _next)| curr)
188	}
189
190	#[inline]
191	fn ready_find_map<'a, F, U>(self, f: F) -> impl Future<Output = Option<U>> + Send
192	where
193		Self: Send + Unpin + 'a,
194		F: Fn(Item) -> Option<U> + Send + 'a,
195		Item: Send,
196		U: Send,
197	{
198		self.ready_filter_map(f)
199			.take(1)
200			.into_future()
201			.map(|(curr, _next)| curr)
202	}
203
204	#[inline]
205	fn ready_filter<'a, F>(
206		self,
207		f: F,
208	) -> Filter<Self, Ready<bool>, impl FnMut(&Item) -> Ready<bool> + 'a>
209	where
210		F: Fn(&Item) -> bool + 'a,
211	{
212		self.filter(move |t| ready(f(t)))
213	}
214
215	#[inline]
216	fn ready_filter_map<F, U>(
217		self,
218		f: F,
219	) -> FilterMap<Self, Ready<Option<U>>, impl FnMut(Item) -> Ready<Option<U>>>
220	where
221		F: Fn(Item) -> Option<U>,
222	{
223		self.filter_map(move |t| ready(f(t)))
224	}
225
226	#[inline]
227	fn ready_fold<T, F>(
228		self,
229		init: T,
230		f: F,
231	) -> Fold<Self, Ready<T>, T, impl FnMut(T, Item) -> Ready<T>>
232	where
233		F: Fn(T, Item) -> T,
234	{
235		self.fold(init, move |a, t| ready(f(a, t)))
236	}
237
238	#[inline]
239	fn ready_fold_default<T, F>(
240		self,
241		f: F,
242	) -> Fold<Self, Ready<T>, T, impl FnMut(T, Item) -> Ready<T>>
243	where
244		F: Fn(T, Item) -> T,
245		T: Default,
246	{
247		self.ready_fold(T::default(), f)
248	}
249
250	#[inline]
251	#[expect(clippy::unit_arg)]
252	fn ready_for_each<F>(
253		self,
254		mut f: F,
255	) -> ForEach<Self, Ready<()>, impl FnMut(Item) -> Ready<()>>
256	where
257		F: FnMut(Item),
258	{
259		self.for_each(move |t| ready(f(t)))
260	}
261
262	#[inline]
263	fn ready_take_while<'a, F>(
264		self,
265		f: F,
266	) -> TakeWhile<Self, Ready<bool>, impl FnMut(&Item) -> Ready<bool> + 'a>
267	where
268		F: Fn(&Item) -> bool + 'a,
269	{
270		self.take_while(move |t| ready(f(t)))
271	}
272
273	#[inline]
274	fn ready_scan<B, T, F>(
275		self,
276		init: T,
277		f: F,
278	) -> Scan<Self, T, Ready<Option<B>>, impl FnMut(&mut T, Item) -> Ready<Option<B>>>
279	where
280		F: Fn(&mut T, Item) -> Option<B>,
281	{
282		self.scan(init, move |s, t| ready(f(s, t)))
283	}
284
285	#[inline]
286	fn ready_scan_each<T, F>(
287		self,
288		init: T,
289		f: F,
290	) -> Scan<Self, T, Ready<Option<Item>>, impl FnMut(&mut T, Item) -> Ready<Option<Item>>>
291	where
292		F: Fn(&mut T, &Item),
293	{
294		self.ready_scan(init, move |s, t| {
295			f(s, &t);
296			Some(t)
297		})
298	}
299
300	#[inline]
301	fn ready_skip_while<'a, F>(
302		self,
303		f: F,
304	) -> SkipWhile<Self, Ready<bool>, impl FnMut(&Item) -> Ready<bool> + 'a>
305	where
306		F: Fn(&Item) -> bool + 'a,
307	{
308		self.skip_while(move |t| ready(f(t)))
309	}
310}