completion/future/join/tuple/race_ok.rs
1use core::marker::PhantomData;
2use core::pin::Pin;
3use core::task::{Context, Poll};
4
5use completion_core::CompletionFuture;
6use pin_project_lite::pin_project;
7
8use super::super::{ControlFlow, RaceOkFuture, TryFuture};
9use super::base::{Join, JoinTuple};
10
11/// Wait for the first future in a tuple to successfully complete.
12///
13/// Requires the `std` feature, as [`std::panic::catch_unwind`] is needed when polling the futures
14/// to ensure soundness.
15///
16/// # Examples
17///
18/// ```
19/// use completion::{future, completion_async};
20/// use futures_lite::future::yield_now;
21///
22/// # future::block_on(completion_async! {
23/// assert_eq!(
24/// future::race_ok((
25/// completion_async!(Err(())),
26/// completion_async!(Ok::<_, ()>(3)),
27/// std::future::pending::<Result<_, ()>>(),
28/// ))
29/// .await,
30/// Ok(3),
31/// );
32/// # });
33/// ```
34///
35/// If all the futures fail, this will return a tuple of the errors.
36///
37/// ```
38/// use completion::{future, completion_async};
39/// use futures_lite::future::yield_now;
40///
41/// # future::block_on(completion_async! {
42/// assert_eq!(
43/// future::race_ok((
44/// completion_async!(Err::<(), _>(0)),
45/// completion_async!(Err(1)),
46/// completion_async!(Err(2)),
47/// ))
48/// .await,
49/// Err((0, 1, 2)),
50/// );
51/// # });
52/// ```
53///
54/// If multiple futures are immediately successfully ready, the earlier one will be chosen. However
55/// after this polling will be fair.
56///
57/// ```
58/// use completion::{future, completion_async};
59/// use futures_lite::future::yield_now;
60///
61/// # future::block_on(completion_async! {
62/// assert_eq!(
63/// future::race_ok((
64/// completion_async! {
65/// yield_now().await;
66/// Ok::<_, ()>(0)
67/// },
68/// completion_async!(Ok::<_, ()>(1)),
69/// completion_async!(Err(2)),
70/// completion_async!(Ok::<_, ()>(3)),
71/// ))
72/// .await,
73/// Ok(1),
74/// );
75/// # });
76/// ```
77#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
78pub fn race_ok<T: RaceOkTuple>(futures: T) -> RaceOk<T> {
79 RaceOk {
80 inner: Join::new(futures.into_tuple()),
81 _correct_debug_bounds: PhantomData,
82 }
83}
84
85pin_project! {
86 /// Future for [`race_ok`].
87 #[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
88 #[derive(Debug)]
89 pub struct RaceOk<T: RaceOkTuple> {
90 #[pin]
91 inner: Join<T::JoinTuple>,
92 _correct_debug_bounds: PhantomData<(T::Futures, T::Ok)>,
93 }
94}
95
96impl<T: RaceOkTuple> CompletionFuture for RaceOk<T> {
97 type Output = Result<T::Ok, <T::JoinTuple as JoinTuple>::Output>;
98
99 unsafe fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
100 self.project().inner.poll(cx).map(|flow| match flow {
101 ControlFlow::Continue(errors) => Err(errors),
102 ControlFlow::Break(val) => Ok(val),
103 })
104 }
105 unsafe fn poll_cancel(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
106 self.project().inner.poll_cancel(cx)
107 }
108}
109
110/// A tuple of futures that can be used in `RaceOk`.
111pub trait RaceOkTuple {
112 /// The tuple that can be used with `Join`.
113 type JoinTuple: JoinTuple<Futures = Self::Futures, Break = Self::Ok>;
114 fn into_tuple(self) -> Self::JoinTuple;
115
116 type Futures;
117 type Ok;
118}
119
120macro_rules! impl_race_tuple {
121 ($($param:ident),*) => {
122 impl<Ok, $($param,)*> RaceOkTuple for ($($param,)*)
123 where
124 $($param: TryFuture<Ok = Ok>,)*
125 {
126 type JoinTuple = ($(RaceOkFuture<$param>,)*);
127 fn into_tuple(self) -> Self::JoinTuple {
128 let ($($param,)*) = self;
129 ($(RaceOkFuture::new($param),)*)
130 }
131
132 type Futures = <Self::JoinTuple as JoinTuple>::Futures;
133 type Ok = Ok;
134 }
135 }
136}
137apply_on_tuples!(impl_race_tuple!);