fidl_next_bind/future/
two_way.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use core::future::Future;
6use core::marker::PhantomData;
7use core::pin::Pin;
8use core::task::{Context, Poll, ready};
9
10use fidl_next_codec::{Decode, DecoderExt, EncodeError};
11use fidl_next_protocol::Transport;
12use pin_project::pin_project;
13
14use crate::{Error, Method, Response};
15
16#[pin_project(project = TwoWayFutureStateProj, project_replace = TwoWayFutureStateOwn)]
17enum TwoWayFutureState<'a, T: Transport> {
18    EncodeError(EncodeError),
19    SendRequest(fidl_next_protocol::TwoWayRequestFuture<'a, T>),
20    SendingRequest(#[pin] fidl_next_protocol::TwoWayRequestFuture<'a, T>),
21    ReceiveResponse(fidl_next_protocol::TwoWayResponseFuture<'a, T>),
22    ReceivingResponse(#[pin] fidl_next_protocol::TwoWayResponseFuture<'a, T>),
23    DecodeBuffer(T::RecvBuffer),
24    Finished,
25}
26
27macro_rules! impl_two_way_future_state {
28    ($(
29        $variant:ident($ty:ty) => $check:ident $unwrap:ident
30    ),* $(,)?) => {
31        impl<T: Transport> TwoWayFutureState<'_, T> {
32            $(
33                #[allow(dead_code)]
34                fn $check(&self) -> bool {
35                    matches!(self, Self::$variant(_))
36                }
37            )*
38        }
39
40        impl<'a, T: Transport> TwoWayFutureStateOwn<'a, T> {
41            $(
42                #[allow(dead_code)]
43                fn $unwrap(self) -> $ty {
44                    let Self::$variant(value) = self else {
45                        unreachable!()
46                    };
47                    value
48                }
49            )*
50        }
51    };
52}
53
54impl_two_way_future_state! {
55    EncodeError(EncodeError) => is_encode_error unwrap_encode_error,
56    SendRequest(fidl_next_protocol::TwoWayRequestFuture<'a, T>)
57        => is_send_request unwrap_send_request,
58    ReceiveResponse(fidl_next_protocol::TwoWayResponseFuture<'a, T>)
59        => is_receive_response unwrap_receive_response,
60    DecodeBuffer(T::RecvBuffer) => is_decode_buffer unwrap_decode_buffer,
61}
62
63impl<'a, T: Transport> TwoWayFutureState<'a, T> {
64    fn finish(self: Pin<&mut Self>) -> TwoWayFutureStateOwn<'a, T> {
65        self.project_replace(Self::Finished)
66    }
67
68    fn poll_advance(
69        mut self: Pin<&mut Self>,
70        cx: &mut Context<'_>,
71    ) -> Poll<Result<(), Error<T::Error>>> {
72        Poll::Ready(match self.as_mut().project() {
73            TwoWayFutureStateProj::EncodeError(_) => {
74                Err(Error::Encode(self.finish().unwrap_encode_error()))
75            }
76            TwoWayFutureStateProj::SendRequest(_) => {
77                let future = self.as_mut().finish().unwrap_send_request();
78                self.project_replace(Self::SendingRequest(future));
79                Ok(())
80            }
81            TwoWayFutureStateProj::SendingRequest(future) => match ready!(future.poll(cx)) {
82                Ok(future) => {
83                    self.project_replace(Self::ReceiveResponse(future));
84                    Ok(())
85                }
86                Err(error) => {
87                    self.finish();
88                    Err(Error::Protocol(error))
89                }
90            },
91            TwoWayFutureStateProj::ReceiveResponse(_) => {
92                let future = self.as_mut().finish().unwrap_receive_response();
93                self.project_replace(Self::ReceivingResponse(future));
94                Ok(())
95            }
96            TwoWayFutureStateProj::ReceivingResponse(future) => match ready!(future.poll(cx)) {
97                Ok(buffer) => {
98                    self.project_replace(Self::DecodeBuffer(buffer));
99                    Ok(())
100                }
101                Err(error) => {
102                    self.finish();
103                    Err(Error::Protocol(error))
104                }
105            },
106            TwoWayFutureStateProj::DecodeBuffer(_) | TwoWayFutureStateProj::Finished => {
107                panic!("TwoWayFutureState polled after completing");
108            }
109        })
110    }
111
112    fn poll_until(
113        mut self: Pin<&mut Self>,
114        cx: &mut Context<'_>,
115        is_done: impl Fn(&Self) -> bool,
116    ) -> Poll<Result<TwoWayFutureStateOwn<'a, T>, Error<T::Error>>> {
117        while !is_done(&self) {
118            if let Err(error) = ready!(self.as_mut().poll_advance(cx)) {
119                return Poll::Ready(Err(error));
120            }
121        }
122        Poll::Ready(Ok(self.finish()))
123    }
124}
125
126macro_rules! two_way_futures {
127    ($(
128        $(#[$metas:meta])* $future:ident -> $output:ty {
129            $check:ident => |$state:ident| $expr:expr
130        }
131    ),* $(,)?) => {
132        $(
133            $(#[$metas])*
134            #[must_use = "futures do nothing unless polled"]
135            #[pin_project]
136            pub struct $future<
137                'a,
138                M: Method,
139                #[cfg(feature = "fuchsia")] T: Transport = zx::Channel,
140                #[cfg(not(feature = "fuchsia"))] T: Transport,
141            > {
142                #[pin]
143                state: TwoWayFutureState<'a, T>,
144                _method: PhantomData<M>,
145            }
146
147            impl<'a, M, T> Future for $future<'a, M, T>
148            where
149                M: Method,
150                M::Response: Decode<T::RecvBuffer>,
151                T: Transport,
152            {
153                type Output = Result<$output, Error<T::Error>>;
154
155                fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
156                    let $state = ready!(self.project().state.poll_until(
157                        cx,
158                        TwoWayFutureState::$check,
159                    ))?;
160                    Poll::Ready(Ok($expr))
161                }
162            }
163        )*
164    }
165}
166
167two_way_futures! {
168    /// A future which performs a two-way FIDL method call.
169    TwoWayFuture -> Response<M, T> {
170        is_decode_buffer => |state| state.unwrap_decode_buffer().decode()?
171    },
172
173    /// A future which performs a two-way FIDL method call.
174    ///
175    /// This future has already been successfully encoded. It still needs to be
176    /// sent and a response needs to be received.
177    EncodedTwoWayFuture -> Response<M, T> {
178        is_decode_buffer => |state| state.unwrap_decode_buffer().decode()?
179    },
180
181    /// A future which sends a two-way FIDL method call.
182    ///
183    /// This future returns another future which completes the FIDL call.
184    SendTwoWayFuture -> SentTwoWayFuture<'a, M, T> {
185        is_receive_response => |state| SentTwoWayFuture {
186            state: TwoWayFutureState::ReceiveResponse(state.unwrap_receive_response()),
187            _method: PhantomData,
188        }
189    },
190
191    /// A future which performs a two-way FIDL method call.
192    ///
193    /// This future has already been successfully encoded and sent. A response
194    /// still needs to be received.
195    SentTwoWayFuture -> Response<M, T> {
196        is_decode_buffer => |state| state.unwrap_decode_buffer().decode()?
197    },
198
199    /// A future which receives a two-way FIDL method call.
200    ///
201    /// This future returns the response buffer without decoding it first.
202    ReceiveTwoWayFuture -> T::RecvBuffer {
203        is_decode_buffer => |state| state.unwrap_decode_buffer()
204    },
205}
206
207macro_rules! impl_for_futures {
208    (
209        $($futures:ident)*,
210        $encode:item
211    ) => {
212        $(
213            impl<'a, M: Method, T: Transport> $futures<'a, M, T> {
214                $encode
215            }
216        )*
217    }
218}
219
220impl_for_futures! {
221    TwoWayFuture,
222
223    /// Encodes the two-way message.
224    ///
225    /// Returns a future which completes the request, or an error if it failed.
226    pub fn encode(self) -> Result<EncodedTwoWayFuture<'a, M, T>, Error<T::Error>> {
227        Ok(EncodedTwoWayFuture {
228            state: match self.state {
229                TwoWayFutureState::EncodeError(error) => return Err(Error::Encode(error)),
230                state => state,
231            },
232            _method: PhantomData,
233        })
234    }
235}
236
237impl_for_futures! {
238    TwoWayFuture EncodedTwoWayFuture,
239
240    /// Sends the two-way message.
241    ///
242    /// Returns a future which completes the request, or an error if it failed.
243    pub fn send(self) -> SendTwoWayFuture<'a, M, T> {
244        SendTwoWayFuture {
245            state: self.state,
246            _method: PhantomData,
247        }
248    }
249}
250
251impl_for_futures! {
252    TwoWayFuture EncodedTwoWayFuture SentTwoWayFuture,
253
254    /// Receives the response to the two-way message.
255    ///
256    /// Returns the response buffer, or an error if it failed.
257    pub fn receive(self) -> ReceiveTwoWayFuture<'a, M, T> {
258        ReceiveTwoWayFuture {
259            state: self.state,
260            _method: PhantomData,
261        }
262    }
263}
264
265impl<'a, M: Method, T: Transport> TwoWayFuture<'a, M, T> {
266    /// Returns a `TwoWayFuture` wrapping the given result.
267    pub fn from_untyped(
268        result: Result<fidl_next_protocol::TwoWayRequestFuture<'a, T>, EncodeError>,
269    ) -> Self {
270        Self {
271            state: match result {
272                Ok(future) => TwoWayFutureState::SendRequest(future),
273                Err(error) => TwoWayFutureState::EncodeError(error),
274            },
275            _method: PhantomData,
276        }
277    }
278}