1use std::{collections::BTreeSet, future::Future, marker::PhantomData};
3
4use prost::Message;
5
6use super::MethodDescriptor;
7
8pub trait Rpc {
9 type Request: Message;
10 type Response: Message + Default;
11 const METHOD: &'static MethodDescriptor;
12}
13pub trait UnaryRpc: Rpc {}
14pub trait ServerStreamingRpc: Rpc {}
15pub trait ClientStreamingRpc: Rpc {}
16pub trait BidirectionalRpc: Rpc {}
17
18pub trait MessageReader: Send {
21 type Error: std::error::Error + Send + Sync + 'static;
22 fn next(&mut self) -> impl Future<Output = Result<Option<Vec<u8>>, Self::Error>> + Send;
23 fn cancel(&mut self);
25}
26
27pub trait MessageWriter: Send {
28 type Error: std::error::Error + Send + Sync + 'static;
29 fn send(&mut self, message: Vec<u8>) -> impl Future<Output = Result<(), Self::Error>> + Send;
30 fn finish(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send;
31 fn abort(&mut self);
32}
33
34pub trait RpcTransport: Send + Sync {
38 type Error: std::error::Error + Send + Sync + 'static;
39 type Reader: MessageReader<Error = Self::Error>;
40 type Writer: MessageWriter<Error = Self::Error>;
41 fn unary(
42 &self,
43 method: &'static MethodDescriptor,
44 request: Vec<u8>,
45 ) -> impl Future<Output = Result<Vec<u8>, Self::Error>> + Send;
46 fn observe(
47 &self,
48 method: &'static MethodDescriptor,
49 request: Vec<u8>,
50 ) -> impl Future<Output = Result<Self::Reader, Self::Error>> + Send;
51 fn exchange(
52 &self,
53 method: &'static MethodDescriptor,
54 opening: Vec<u8>,
55 ) -> impl Future<Output = Result<(Self::Writer, Self::Reader), Self::Error>> + Send;
56}
57
58#[derive(Debug, thiserror::Error)]
59pub enum ClientError<E: std::error::Error> {
60 #[error("endpoint does not implement {0}")]
61 NotImplemented(&'static str),
62 #[error("incompatible peer for {0}")]
63 Protocol(&'static str),
64 #[error("{0} requires a stable client operation ID")]
65 MissingOperationId(&'static str),
66 #[error("invalid request metadata: {0}")]
67 Metadata(#[from] crate::RequestMetadataError),
68 #[error("transport failure: {0}")]
69 Transport(E),
70 #[error("invalid protobuf response: {0}")]
71 Decode(#[from] prost::DecodeError),
72}
73
74pub struct Client<T> {
75 transport: T,
76 implemented: BTreeSet<String>,
77 protocol: Option<crate::heddle::api::common::ProtocolCompatibility>,
78}
79
80impl<T: RpcTransport> Client<T> {
81 pub fn new(transport: T, implemented: impl IntoIterator<Item = String>) -> Self {
84 Self {
85 transport,
86 implemented: implemented.into_iter().collect(),
87 protocol: None,
88 }
89 }
90
91 pub fn with_protocol(
95 mut self,
96 protocol: crate::heddle::api::common::ProtocolCompatibility,
97 ) -> Self {
98 self.protocol = Some(protocol);
99 self
100 }
101
102 fn encode<M: Rpc>(&self, request: &M::Request) -> Result<Vec<u8>, ClientError<T::Error>> {
103 let method = M::METHOD;
104 if !self.implemented.contains(method.path) {
105 return Err(ClientError::NotImplemented(method.path));
106 }
107 if !method.mandatory_features.is_empty() {
108 crate::import_authority::require_hybrid_peer(self.protocol.as_ref())
109 .map_err(|_| ClientError::Protocol(method.path))?;
110 }
111 let bytes = request.encode_to_vec();
112 validate_stream_protocol(method, &bytes, true, true)
113 .map_err(|_| ClientError::Protocol(method.path))?;
114 if method.client_operation_id_required {
115 let Some(field) = method.client_operation_id_field_number else {
116 return Err(ClientError::MissingOperationId(method.path));
117 };
118 let id = crate::transport::protobuf_string_field(&bytes, field)?;
119 if id.is_none_or(|value| value.trim().is_empty()) {
120 return Err(ClientError::MissingOperationId(method.path));
121 }
122 }
123 Ok(bytes)
124 }
125
126 pub async fn call<M: UnaryRpc>(
131 &self,
132 request: &M::Request,
133 ) -> Result<M::Response, ClientError<T::Error>> {
134 let bytes = self
135 .transport
136 .unary(M::METHOD, self.encode::<M>(request)?)
137 .await
138 .map_err(ClientError::Transport)?;
139 Ok(M::Response::decode(bytes.as_slice())?)
140 }
141
142 pub async fn observe<M: ServerStreamingRpc>(
143 &self,
144 request: &M::Request,
145 ) -> Result<Messages<T::Reader, M::Response>, ClientError<T::Error>> {
146 let reader = self
147 .transport
148 .observe(M::METHOD, self.encode::<M>(request)?)
149 .await
150 .map_err(ClientError::Transport)?;
151 Ok(Messages {
152 reader,
153 method: M::METHOD,
154 first: true,
155 done: false,
156 message: PhantomData,
157 })
158 }
159
160 pub async fn exchange<M: BidirectionalRpc>(
162 &self,
163 opening: &M::Request,
164 ) -> Result<
165 (
166 Sender<T::Writer, M::Request>,
167 Messages<T::Reader, M::Response>,
168 ),
169 ClientError<T::Error>,
170 > {
171 let (writer, reader) = self
172 .transport
173 .exchange(M::METHOD, self.encode::<M>(opening)?)
174 .await
175 .map_err(ClientError::Transport)?;
176 Ok((
177 Sender {
178 writer,
179 method: M::METHOD,
180 finished: false,
181 message: PhantomData,
182 },
183 Messages {
184 reader,
185 method: M::METHOD,
186 first: true,
187 done: false,
188 message: PhantomData,
189 },
190 ))
191 }
192}
193
194pub struct Messages<R: MessageReader, O> {
195 reader: R,
196 method: &'static MethodDescriptor,
197 first: bool,
198 done: bool,
199 message: PhantomData<O>,
200}
201
202impl<R: MessageReader, O> Messages<R, O> {
203 pub fn cancel(&mut self) {
206 if !self.done {
207 self.reader.cancel();
208 self.done = true;
209 }
210 }
211}
212
213impl<R: MessageReader, O: Message + Default> Messages<R, O> {
214 pub async fn next(&mut self) -> Result<Option<O>, ClientError<R::Error>> {
215 if self.done {
216 return Ok(None);
217 }
218 let decoded = match self.reader.next().await {
219 Ok(Some(bytes)) => {
220 if validate_stream_protocol(self.method, &bytes, false, self.first).is_err() {
221 Err(ClientError::Protocol(self.method.path))
222 } else {
223 self.first = false;
224 O::decode(bytes.as_slice())
225 .map(Some)
226 .map_err(ClientError::Decode)
227 }
228 }
229 Ok(None) => {
230 if self.first && is_hybrid_stream(self.method) {
231 Err(ClientError::Protocol(self.method.path))
232 } else {
233 self.done = true;
234 Ok(None)
235 }
236 }
237 Err(error) => Err(ClientError::Transport(error)),
238 };
239 if decoded.is_err() {
240 self.reader.cancel();
241 self.done = true;
242 }
243 decoded
244 }
245}
246
247impl<R: MessageReader, O> Drop for Messages<R, O> {
248 fn drop(&mut self) {
249 self.cancel();
250 }
251}
252
253pub struct Sender<W: MessageWriter, I> {
254 writer: W,
255 method: &'static MethodDescriptor,
256 finished: bool,
257 message: PhantomData<I>,
258}
259
260impl<W: MessageWriter, I: Message> Sender<W, I> {
261 pub async fn send(&mut self, message: &I) -> Result<(), ClientError<W::Error>> {
262 let bytes = message.encode_to_vec();
263 validate_stream_protocol(self.method, &bytes, true, false)
264 .map_err(|_| ClientError::Protocol(self.method.path))?;
265 self.writer
266 .send(bytes)
267 .await
268 .map_err(ClientError::Transport)
269 }
270 pub async fn finish(mut self) -> Result<(), W::Error> {
271 self.writer.finish().await?;
272 self.finished = true;
273 Ok(())
274 }
275}
276
277impl<W: MessageWriter, I> Drop for Sender<W, I> {
278 fn drop(&mut self) {
279 if !self.finished {
280 self.writer.abort();
281 }
282 }
283}
284
285fn is_hybrid_stream(method: &MethodDescriptor) -> bool {
288 method.path.starts_with("/heddle.api.v1alpha2.SyncService/")
289 && !method.mandatory_features.is_empty()
290}
291fn validate_stream_protocol(
293 method: &MethodDescriptor,
294 bytes: &[u8],
295 request: bool,
296 first: bool,
297) -> Result<(), crate::hybrid_codec::Reject> {
298 use crate::heddle::api::v1alpha2::*;
299 use crate::hybrid_codec::Reject;
300 use crate::import_authority::require_hybrid_peer;
301 if !is_hybrid_stream(method) {
302 return Ok(());
303 }
304 macro_rules! frame {
305 ($ty:ty, $variant:path) => {{
306 let value = <$ty>::decode(bytes).map_err(|_| Reject::Protocol)?;
307 match value.body {
308 Some($variant(open)) => require_hybrid_peer(open.protocol.as_ref()),
309 _ if first => Err(Reject::Protocol),
310 _ => Ok(()),
311 }
312 }};
313 }
314 match (method.path.rsplit('/').next(), request) {
315 (Some("Fetch"), true) => {
316 frame!(FetchClientFrame, fetch_client_frame::Body::Open)
317 }
318 (Some("Fetch"), false) => frame!(FetchServerFrame, fetch_server_frame::Body::Ready),
319 (Some("PublishContent"), true) => frame!(
320 PublishContentClientFrame,
321 publish_content_client_frame::Body::Open
322 ),
323 (Some("PublishContent"), false) => frame!(
324 PublishContentServerFrame,
325 publish_content_server_frame::Body::Ready
326 ),
327 (Some("ReplicateThread"), true) => {
328 frame!(ReplicateThreadRequest, replicate_thread_request::Body::Open)
329 }
330 (Some("ReplicateThread"), false) => frame!(
331 ReplicateThreadResponse,
332 replicate_thread_response::Body::Ready
333 ),
334 _ => Err(Reject::Protocol),
335 }
336}