Skip to main content

fidl_next_bind/future/
send.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::pin::Pin;
7use core::task::{Context, Poll, ready};
8
9use fidl_next_codec::EncodeError;
10use fidl_next_protocol::{NonBlockingTransport, Transport};
11use pin_project::pin_project;
12
13use crate::Error;
14
15macro_rules! define_send_future {
16    (
17        $(#[$metas:meta])*
18        $name:ident<$($lifetime:lifetime,)? T: Transport>($future:ty) => $encoded:ident {
19            state = $state:ident,
20            proj = $proj:ident,
21            own = $own:ident,
22        }
23    ) => {
24        #[pin_project(project = $proj, project_replace = $own)]
25        enum $state<$($lifetime,)? T: Transport> {
26            EncodeError(EncodeError),
27            Sending(#[pin] $future),
28            Finished,
29        }
30
31        impl<$($lifetime,)? T: Transport> $state<$($lifetime,)? T> {
32            fn poll_state(
33                mut self: Pin<&mut Self>,
34                cx: &mut Context<'_>,
35            ) -> Poll<Result<(), Error<T::Error>>> {
36                match self.as_mut().project() {
37                    $proj::EncodeError(_) => {
38                        let state = self.project_replace(Self::Finished);
39                        let $own::EncodeError(error) = state else {
40                            unreachable!();
41                        };
42                        Poll::Ready(Err(Error::Encode(error)))
43                    }
44                    $proj::Sending(future) => match ready!(future.poll(cx)) {
45                        Ok(()) => Poll::Ready(Ok(())),
46                        Err(error) => Poll::Ready(Err(Error::Protocol(error))),
47                    },
48                    $proj::Finished => panic!("State polled after completing"),
49                }
50            }
51
52            fn send_immediately(self) -> Result<(), Error<T::Error>>
53            where
54                T: NonBlockingTransport,
55            {
56                match self {
57                    Self::EncodeError(e) => return Err(e.into()),
58                    Self::Sending(future) => future.send_immediately()?,
59                    Self::Finished => unreachable!(),
60                }
61
62                Ok(())
63            }
64        }
65
66        $(#[$metas])*
67        #[must_use = "futures do nothing unless polled"]
68        #[pin_project]
69        pub struct $name<
70            $($lifetime,)?
71            T: Transport,
72        > {
73            #[pin]
74            state: $state<$($lifetime,)? T>,
75        }
76
77        impl<$($lifetime,)? T: Transport> $name<$($lifetime,)? T> {
78            #[doc = concat!("Returns a `", stringify!($name), "` wrapping the given result.")]
79            pub fn from_untyped(result: Result<$future, EncodeError>) -> Self {
80                Self {
81                    state: match result {
82                        Err(error) => $state::EncodeError(error),
83                        Ok(future) => $state::Sending(future),
84                    },
85                }
86            }
87
88            /// Encodes the message.
89            ///
90            /// Returns a future which sends the message, or an error if it failed.
91            pub fn encode(self) -> Result<$encoded<$($lifetime,)? T>, Error<T::Error>> {
92                Ok($encoded {
93                    state: match self.state {
94                        $state::EncodeError(error) => return Err(Error::Encode(error)),
95                        state => state,
96                    },
97                })
98            }
99
100            /// Completes the send operation synchronously and without blocking.
101            ///
102            /// Using this method prevents transports from applying
103            /// backpressure. Prefer awaiting when possible to allow for
104            /// backpressure.
105            ///
106            /// Because failed sends return immediately, `send_immediately` may
107            /// observe transport closure prematurely. This can manifest as this
108            /// method returning `Err(PeerClosed)` or `Err(Stopped)` when it
109            /// should have returned `Err(PeerClosedWithEpitaph)`. Prefer
110            /// awaiting when possible for correctness.
111            pub fn send_immediately(self) -> Result<(), Error<T::Error>>
112            where
113                T: NonBlockingTransport,
114            {
115                self.state.send_immediately()
116            }
117        }
118
119        impl<'a, T: Transport> Future for $name<$($lifetime,)? T> {
120            type Output = Result<(), Error<T::Error>>;
121
122            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
123                self.project().state.poll_state(cx)
124            }
125        }
126
127        #[doc = concat!("An encoded `", stringify!($name), "`.")]
128        ///
129        /// This future has already been successfully encoded. It still needs to be
130        /// sent.
131        #[must_use = "futures do nothing unless polled"]
132        #[pin_project]
133        pub struct $encoded<
134            $($lifetime,)?
135            T: Transport,
136        > {
137            #[pin]
138            state: $state<$($lifetime,)? T>,
139        }
140
141        impl<$($lifetime,)? T: Transport> Future for $encoded<$($lifetime,)? T> {
142            type Output = Result<(), Error<T::Error>>;
143
144            fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
145                self.project().state.poll_state(cx)
146            }
147        }
148    }
149}
150
151define_send_future! {
152    /// A future which sends an encoded message to a connection.
153    SendFuture<'a, T: Transport>(fidl_next_protocol::SendFuture<'a, T>) => EncodedSendFuture {
154        state = SendFutureState,
155        proj = SendFutureProj,
156        own = SendFutureOwn,
157    }
158}
159
160define_send_future! {
161    /// A future which responds to a request with an encoded message.
162    RespondFuture<T: Transport>(fidl_next_protocol::RespondFuture<T>) => EncodedRespondFuture {
163        state = RespondFutureState,
164        proj = RespondFutureProj,
165        own = RespondFutureOwn,
166    }
167}