starpc 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,517 @@
1
+ //! RpcStream trait and functions for opening/handling nested RPC streams.
2
+
3
+ use async_trait::async_trait;
4
+ use bytes::Bytes;
5
+ use prost::Message;
6
+ use std::sync::Arc;
7
+
8
+ use crate::client::{OpenStream, PacketReceiver};
9
+ use crate::error::{Error, Result};
10
+ use crate::invoker::Invoker;
11
+ use crate::proto::Packet;
12
+ use crate::rpc::{PacketWriter, ServerRpc};
13
+ use crate::stream::{Context, Stream};
14
+
15
+ use super::proto::{RpcStreamPacket, RpcStreamPacketBody};
16
+ use super::writer::RpcStreamWriter;
17
+
18
+ /// RpcStream is a bidirectional stream for RpcStreamPacket messages.
19
+ ///
20
+ /// This trait extends the base Stream trait with typed send/recv operations
21
+ /// for RpcStreamPacket messages.
22
+ #[async_trait]
23
+ pub trait RpcStream: Stream {
24
+ /// Sends an RpcStreamPacket.
25
+ async fn send_packet(&self, packet: &RpcStreamPacket) -> Result<()>;
26
+
27
+ /// Receives an RpcStreamPacket.
28
+ async fn recv_packet(&self) -> Result<RpcStreamPacket>;
29
+ }
30
+
31
+ /// Generic RpcStream implementation wrapping any Stream.
32
+ pub struct RpcStreamImpl<S: Stream> {
33
+ inner: S,
34
+ }
35
+
36
+ impl<S: Stream> RpcStreamImpl<S> {
37
+ /// Creates a new RpcStreamImpl wrapping the given stream.
38
+ pub fn new(inner: S) -> Self {
39
+ Self { inner }
40
+ }
41
+ }
42
+
43
+ #[async_trait]
44
+ impl<S: Stream + Send + Sync> Stream for RpcStreamImpl<S> {
45
+ fn context(&self) -> &Context {
46
+ self.inner.context()
47
+ }
48
+
49
+ async fn send_bytes(&self, data: Bytes) -> Result<()> {
50
+ self.inner.send_bytes(data).await
51
+ }
52
+
53
+ async fn recv_bytes(&self) -> Result<Bytes> {
54
+ self.inner.recv_bytes().await
55
+ }
56
+
57
+ async fn close_send(&self) -> Result<()> {
58
+ self.inner.close_send().await
59
+ }
60
+
61
+ async fn close(&self) -> Result<()> {
62
+ self.inner.close().await
63
+ }
64
+ }
65
+
66
+ #[async_trait]
67
+ impl<S: Stream + Send + Sync> RpcStream for RpcStreamImpl<S> {
68
+ async fn send_packet(&self, packet: &RpcStreamPacket) -> Result<()> {
69
+ let data = packet.encode_to_vec();
70
+ self.inner.send_bytes(Bytes::from(data)).await
71
+ }
72
+
73
+ async fn recv_packet(&self) -> Result<RpcStreamPacket> {
74
+ let data = self.inner.recv_bytes().await?;
75
+ RpcStreamPacket::decode(&data[..]).map_err(Error::InvalidMessage)
76
+ }
77
+ }
78
+
79
+ // Implement RpcStream for Arc<S> where S: RpcStream
80
+ #[async_trait]
81
+ impl<S: RpcStream + ?Sized + Send + Sync> RpcStream for Arc<S> {
82
+ async fn send_packet(&self, packet: &RpcStreamPacket) -> Result<()> {
83
+ (**self).send_packet(packet).await
84
+ }
85
+
86
+ async fn recv_packet(&self) -> Result<RpcStreamPacket> {
87
+ (**self).recv_packet().await
88
+ }
89
+ }
90
+
91
+ /// Getter function to resolve component ID to an invoker.
92
+ ///
93
+ /// # Arguments
94
+ /// * `ctx` - Context for the RPC stream
95
+ /// * `component_id` - The component ID to look up
96
+ /// * `released` - Callback to invoke when the stream is released
97
+ ///
98
+ /// # Returns
99
+ /// `Some((invoker, release_fn))` if found, `None` if not found.
100
+ pub type RpcStreamGetter = Arc<
101
+ dyn Fn(&Context, &str, Box<dyn FnOnce() + Send>) -> Option<(Arc<dyn Invoker>, Box<dyn FnOnce() + Send>)>
102
+ + Send
103
+ + Sync,
104
+ >;
105
+
106
+ /// Opens an RPC stream with a remote component.
107
+ ///
108
+ /// This function performs the client-side init/ack handshake:
109
+ /// 1. Sends RpcStreamInit with the component ID
110
+ /// 2. Optionally waits for RpcAck from the server
111
+ ///
112
+ /// # Arguments
113
+ /// * `stream` - The underlying RPC stream
114
+ /// * `component_id` - The target component ID
115
+ /// * `wait_ack` - Whether to wait for acknowledgment
116
+ ///
117
+ /// # Returns
118
+ /// Ok(()) on success
119
+ pub async fn open_rpc_stream<S: RpcStream + Send + Sync>(
120
+ stream: &S,
121
+ component_id: &str,
122
+ wait_ack: bool,
123
+ ) -> Result<()> {
124
+ // Send the init packet
125
+ let init_packet = RpcStreamPacket::new_init(component_id.to_string());
126
+ stream.send_packet(&init_packet).await?;
127
+
128
+ // Wait for ack if requested
129
+ if wait_ack {
130
+ let ack_packet = stream.recv_packet().await?;
131
+ match ack_packet.body {
132
+ Some(RpcStreamPacketBody::Ack(ack)) => {
133
+ if !ack.error.is_empty() {
134
+ return Err(Error::Remote(format!("remote: {}", ack.error)));
135
+ }
136
+ }
137
+ _ => {
138
+ return Err(Error::UnrecognizedPacket);
139
+ }
140
+ }
141
+ }
142
+
143
+ Ok(())
144
+ }
145
+
146
+ /// Handles an incoming RPC stream (server side).
147
+ ///
148
+ /// This function handles the server-side of the rpcstream protocol:
149
+ /// 1. Receives RpcStreamInit with component ID
150
+ /// 2. Looks up the invoker for that component
151
+ /// 3. Sends RpcAck (with error if not found)
152
+ /// 4. Handles nested RPC calls over the stream
153
+ ///
154
+ /// # Arguments
155
+ /// * `stream` - The incoming RPC stream
156
+ /// * `getter` - Function to look up invokers by component ID
157
+ pub async fn handle_rpc_stream<S: RpcStream + Send + Sync + 'static>(
158
+ stream: Arc<S>,
159
+ getter: RpcStreamGetter,
160
+ ) -> Result<()> {
161
+ // Read the init packet
162
+ let init_packet = stream.recv_packet().await?;
163
+ let component_id = match init_packet.body {
164
+ Some(RpcStreamPacketBody::Init(init)) => init.component_id,
165
+ _ => {
166
+ return Err(Error::UnrecognizedPacket);
167
+ }
168
+ };
169
+
170
+ let ctx = stream.context().child();
171
+
172
+ // Look up the invoker
173
+ let ctx_cancel = ctx.clone();
174
+ let released = Box::new(move || {
175
+ ctx_cancel.cancel();
176
+ });
177
+
178
+ let lookup_result = getter(&ctx, &component_id, released);
179
+
180
+ // Send ack
181
+ let (invoker, release_fn) = match lookup_result {
182
+ Some((inv, rel)) => {
183
+ // Send success ack
184
+ stream
185
+ .send_packet(&RpcStreamPacket::new_ack(String::new()))
186
+ .await?;
187
+ (inv, Some(rel))
188
+ }
189
+ None => {
190
+ // Send error ack
191
+ let err_msg = format!("no server for component: {}", component_id);
192
+ stream
193
+ .send_packet(&RpcStreamPacket::new_ack(err_msg.clone()))
194
+ .await?;
195
+ return Err(Error::Remote(err_msg));
196
+ }
197
+ };
198
+
199
+ // Ensure release is called when we're done
200
+ let _release_guard = scopeguard::guard(release_fn, |rel| {
201
+ if let Some(f) = rel {
202
+ f();
203
+ }
204
+ });
205
+
206
+ // Create a writer for the RPC stream
207
+ let writer: Arc<dyn PacketWriter> = Arc::new(RpcStreamWriter::new(stream.clone()));
208
+
209
+ // Read and handle packets
210
+ loop {
211
+ let rpc_packet = match stream.recv_packet().await {
212
+ Ok(p) => p,
213
+ Err(Error::StreamClosed) => break,
214
+ Err(e) => return Err(e),
215
+ };
216
+
217
+ let packet = match rpc_packet.body {
218
+ Some(RpcStreamPacketBody::Data(data)) => {
219
+ match Packet::decode(&data[..]) {
220
+ Ok(p) => p,
221
+ Err(e) => return Err(Error::InvalidMessage(e)),
222
+ }
223
+ }
224
+ _ => continue, // Ignore non-data packets
225
+ };
226
+
227
+ // Handle the packet based on its type
228
+ use crate::proto::packet::Body;
229
+ match packet.body {
230
+ Some(Body::CallStart(call_start)) => {
231
+ let rpc_ctx = ctx.child();
232
+ let rpc = Arc::new(ServerRpc::from_call_start(
233
+ rpc_ctx,
234
+ call_start,
235
+ writer.clone(),
236
+ ));
237
+
238
+ let service_id = rpc.service().to_string();
239
+ let method_id = rpc.method().to_string();
240
+
241
+ // Spawn a task to handle this RPC
242
+ let invoker_clone = invoker.clone();
243
+ tokio::spawn(async move {
244
+ let (_found, _result) = invoker_clone
245
+ .invoke_method(&service_id, &method_id, Box::new(ServerRpcStream { rpc }))
246
+ .await;
247
+ });
248
+ }
249
+ Some(Body::CallData(_)) | Some(Body::CallCancel(_)) => {
250
+ // These should be routed to an existing RPC
251
+ // In this simplified implementation, they're ignored
252
+ }
253
+ None => {}
254
+ }
255
+ }
256
+
257
+ Ok(())
258
+ }
259
+
260
+ /// Wrapper to make ServerRpc implement Stream for use with invoke_method.
261
+ struct ServerRpcStream {
262
+ rpc: Arc<ServerRpc>,
263
+ }
264
+
265
+ #[async_trait]
266
+ impl Stream for ServerRpcStream {
267
+ fn context(&self) -> &Context {
268
+ self.rpc.context()
269
+ }
270
+
271
+ async fn send_bytes(&self, data: Bytes) -> Result<()> {
272
+ crate::stream::Stream::send_bytes(self.rpc.as_ref(), data).await
273
+ }
274
+
275
+ async fn recv_bytes(&self) -> Result<Bytes> {
276
+ crate::stream::Stream::recv_bytes(self.rpc.as_ref()).await
277
+ }
278
+
279
+ async fn close_send(&self) -> Result<()> {
280
+ crate::stream::Stream::close_send(self.rpc.as_ref()).await
281
+ }
282
+
283
+ async fn close(&self) -> Result<()> {
284
+ crate::stream::Stream::close(self.rpc.as_ref()).await
285
+ }
286
+ }
287
+
288
+ /// Creates an OpenStream function using an RPC stream caller.
289
+ ///
290
+ /// This allows creating a Client that operates over an RPC stream,
291
+ /// enabling nested RPC calls.
292
+ ///
293
+ /// # Type Parameters
294
+ /// * `F` - Async function that creates a new stream
295
+ /// * `S` - Stream type
296
+ pub fn new_rpc_stream_open_stream<F, Fut, S>(
297
+ caller: F,
298
+ component_id: String,
299
+ wait_ack: bool,
300
+ ) -> impl OpenStream
301
+ where
302
+ F: Fn() -> Fut + Send + Sync + 'static,
303
+ Fut: std::future::Future<Output = Result<S>> + Send + 'static,
304
+ S: Stream + Send + Sync + 'static,
305
+ {
306
+ RpcStreamOpener {
307
+ caller: Arc::new(caller),
308
+ component_id,
309
+ wait_ack,
310
+ _phantom: std::marker::PhantomData,
311
+ }
312
+ }
313
+
314
+ struct RpcStreamOpener<F, S> {
315
+ caller: Arc<F>,
316
+ component_id: String,
317
+ wait_ack: bool,
318
+ _phantom: std::marker::PhantomData<S>,
319
+ }
320
+
321
+ #[async_trait]
322
+ impl<F, Fut, S> OpenStream for RpcStreamOpener<F, S>
323
+ where
324
+ F: Fn() -> Fut + Send + Sync + 'static,
325
+ Fut: std::future::Future<Output = Result<S>> + Send + 'static,
326
+ S: Stream + Send + Sync + 'static,
327
+ {
328
+ async fn open_stream(&self) -> Result<(Arc<dyn PacketWriter>, PacketReceiver)> {
329
+ // Open the underlying stream
330
+ let stream = (self.caller)().await?;
331
+ let rpc_stream = Arc::new(RpcStreamImpl::new(stream));
332
+
333
+ // Perform the init/ack handshake
334
+ open_rpc_stream(rpc_stream.as_ref(), &self.component_id, self.wait_ack).await?;
335
+
336
+ // Create a writer
337
+ let writer: Arc<dyn PacketWriter> = Arc::new(RpcStreamWriter::new(rpc_stream.clone()));
338
+
339
+ // Create a channel for incoming packets
340
+ let (tx, rx) = tokio::sync::mpsc::channel(32);
341
+
342
+ // Spawn a read pump to convert RpcStreamPacket::Data into Packets
343
+ let stream_clone = rpc_stream.clone();
344
+ tokio::spawn(async move {
345
+ loop {
346
+ match stream_clone.recv_packet().await {
347
+ Ok(packet) => {
348
+ if let Some(RpcStreamPacketBody::Data(data)) = packet.body {
349
+ match Packet::decode(&data[..]) {
350
+ Ok(p) => {
351
+ if tx.send(p).await.is_err() {
352
+ break;
353
+ }
354
+ }
355
+ Err(_) => break,
356
+ }
357
+ }
358
+ }
359
+ Err(_) => break,
360
+ }
361
+ }
362
+ });
363
+
364
+ Ok((writer, rx))
365
+ }
366
+ }
367
+
368
+ /// Creates a Client that operates over an RPC stream.
369
+ ///
370
+ /// # Arguments
371
+ /// * `caller` - Function that opens a new RPC stream
372
+ /// * `component_id` - Target component ID
373
+ /// * `wait_ack` - Whether to wait for acknowledgment
374
+ pub fn new_rpc_stream_client<F, Fut, S>(
375
+ caller: F,
376
+ component_id: String,
377
+ wait_ack: bool,
378
+ ) -> crate::SrpcClient<impl OpenStream>
379
+ where
380
+ F: Fn() -> Fut + Send + Sync + 'static,
381
+ Fut: std::future::Future<Output = Result<S>> + Send + 'static,
382
+ S: Stream + Send + Sync + 'static,
383
+ {
384
+ let opener = new_rpc_stream_open_stream(caller, component_id, wait_ack);
385
+ crate::SrpcClient::new(opener)
386
+ }
387
+
388
+ #[cfg(test)]
389
+ mod tests {
390
+ use super::*;
391
+ use std::sync::atomic::{AtomicBool, Ordering};
392
+ use tokio::sync::Mutex;
393
+ use std::collections::VecDeque;
394
+
395
+ struct MockRpcStream {
396
+ ctx: Context,
397
+ send_queue: Mutex<VecDeque<RpcStreamPacket>>,
398
+ recv_queue: Mutex<VecDeque<RpcStreamPacket>>,
399
+ closed: AtomicBool,
400
+ }
401
+
402
+ impl MockRpcStream {
403
+ fn new() -> Self {
404
+ Self {
405
+ ctx: Context::new(),
406
+ send_queue: Mutex::new(VecDeque::new()),
407
+ recv_queue: Mutex::new(VecDeque::new()),
408
+ closed: AtomicBool::new(false),
409
+ }
410
+ }
411
+
412
+ async fn push_recv(&self, packet: RpcStreamPacket) {
413
+ self.recv_queue.lock().await.push_back(packet);
414
+ }
415
+
416
+ async fn pop_sent(&self) -> Option<RpcStreamPacket> {
417
+ self.send_queue.lock().await.pop_front()
418
+ }
419
+ }
420
+
421
+ #[async_trait]
422
+ impl Stream for MockRpcStream {
423
+ fn context(&self) -> &Context {
424
+ &self.ctx
425
+ }
426
+
427
+ async fn send_bytes(&self, data: Bytes) -> Result<()> {
428
+ let packet = RpcStreamPacket::decode(&data[..]).map_err(Error::InvalidMessage)?;
429
+ self.send_queue.lock().await.push_back(packet);
430
+ Ok(())
431
+ }
432
+
433
+ async fn recv_bytes(&self) -> Result<Bytes> {
434
+ loop {
435
+ if let Some(packet) = self.recv_queue.lock().await.pop_front() {
436
+ return Ok(Bytes::from(packet.encode_to_vec()));
437
+ }
438
+ if self.closed.load(Ordering::SeqCst) {
439
+ return Err(Error::StreamClosed);
440
+ }
441
+ tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
442
+ }
443
+ }
444
+
445
+ async fn close_send(&self) -> Result<()> {
446
+ Ok(())
447
+ }
448
+
449
+ async fn close(&self) -> Result<()> {
450
+ self.closed.store(true, Ordering::SeqCst);
451
+ Ok(())
452
+ }
453
+ }
454
+
455
+ #[async_trait]
456
+ impl RpcStream for MockRpcStream {
457
+ async fn send_packet(&self, packet: &RpcStreamPacket) -> Result<()> {
458
+ self.send_queue.lock().await.push_back(packet.clone());
459
+ Ok(())
460
+ }
461
+
462
+ async fn recv_packet(&self) -> Result<RpcStreamPacket> {
463
+ loop {
464
+ if let Some(packet) = self.recv_queue.lock().await.pop_front() {
465
+ return Ok(packet);
466
+ }
467
+ if self.closed.load(Ordering::SeqCst) {
468
+ return Err(Error::StreamClosed);
469
+ }
470
+ tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
471
+ }
472
+ }
473
+ }
474
+
475
+ #[tokio::test]
476
+ async fn test_open_rpc_stream_no_ack() {
477
+ let stream = MockRpcStream::new();
478
+
479
+ let result = open_rpc_stream(&stream, "test-component", false).await;
480
+ assert!(result.is_ok());
481
+
482
+ // Check that init was sent
483
+ let sent = stream.pop_sent().await.unwrap();
484
+ match sent.body {
485
+ Some(RpcStreamPacketBody::Init(init)) => {
486
+ assert_eq!(init.component_id, "test-component");
487
+ }
488
+ _ => panic!("Expected Init packet"),
489
+ }
490
+ }
491
+
492
+ #[tokio::test]
493
+ async fn test_open_rpc_stream_with_ack() {
494
+ let stream = MockRpcStream::new();
495
+
496
+ // Pre-queue an ack response
497
+ stream
498
+ .push_recv(RpcStreamPacket::new_ack(String::new()))
499
+ .await;
500
+
501
+ let result = open_rpc_stream(&stream, "test-component", true).await;
502
+ assert!(result.is_ok());
503
+ }
504
+
505
+ #[tokio::test]
506
+ async fn test_open_rpc_stream_with_error_ack() {
507
+ let stream = MockRpcStream::new();
508
+
509
+ // Pre-queue an error ack response
510
+ stream
511
+ .push_recv(RpcStreamPacket::new_ack("component not found".to_string()))
512
+ .await;
513
+
514
+ let result = open_rpc_stream(&stream, "test-component", true).await;
515
+ assert!(matches!(result, Err(Error::Remote(_))));
516
+ }
517
+ }
@@ -0,0 +1,150 @@
1
+ //! RpcStreamWriter - PacketWriter implementation for RpcStream.
2
+
3
+ use async_trait::async_trait;
4
+ use bytes::Bytes;
5
+ use prost::Message;
6
+
7
+ use crate::error::Result;
8
+ use crate::proto::Packet;
9
+ use crate::rpc::PacketWriter;
10
+
11
+ use super::proto::RpcStreamPacket;
12
+ use super::stream::RpcStream;
13
+
14
+ /// RpcStreamWriter wraps an RpcStream and implements PacketWriter.
15
+ ///
16
+ /// This allows using an RpcStream as the transport for nested RPC calls,
17
+ /// converting Packet messages to RpcStreamPacket::Data messages.
18
+ pub struct RpcStreamWriter<S> {
19
+ /// The underlying RpcStream.
20
+ pub(crate) inner: S,
21
+ }
22
+
23
+ impl<S> RpcStreamWriter<S> {
24
+ /// Creates a new RpcStreamWriter.
25
+ pub fn new(stream: S) -> Self {
26
+ Self { inner: stream }
27
+ }
28
+
29
+ /// Returns a reference to the inner stream.
30
+ pub fn inner(&self) -> &S {
31
+ &self.inner
32
+ }
33
+ }
34
+
35
+ #[async_trait]
36
+ impl<S: RpcStream + Send + Sync> PacketWriter for RpcStreamWriter<S> {
37
+ async fn write_packet(&self, packet: Packet) -> Result<()> {
38
+ let data = packet.encode_to_vec();
39
+ let rpc_packet = RpcStreamPacket::new_data(Bytes::from(data));
40
+ self.inner.send_packet(&rpc_packet).await
41
+ }
42
+
43
+ async fn close(&self) -> Result<()> {
44
+ self.inner.close_send().await
45
+ }
46
+ }
47
+
48
+
49
+ #[cfg(test)]
50
+ mod tests {
51
+ use super::*;
52
+ use crate::stream::{Context, Stream};
53
+ use std::collections::VecDeque;
54
+ use std::sync::atomic::{AtomicBool, Ordering};
55
+ use tokio::sync::Mutex;
56
+
57
+ struct MockRpcStream {
58
+ ctx: Context,
59
+ packets: Mutex<VecDeque<RpcStreamPacket>>,
60
+ closed: AtomicBool,
61
+ }
62
+
63
+ impl MockRpcStream {
64
+ fn new() -> Self {
65
+ Self {
66
+ ctx: Context::new(),
67
+ packets: Mutex::new(VecDeque::new()),
68
+ closed: AtomicBool::new(false),
69
+ }
70
+ }
71
+
72
+ async fn get_packets(&self) -> Vec<RpcStreamPacket> {
73
+ self.packets.lock().await.iter().cloned().collect()
74
+ }
75
+ }
76
+
77
+ #[async_trait]
78
+ impl Stream for MockRpcStream {
79
+ fn context(&self) -> &Context {
80
+ &self.ctx
81
+ }
82
+
83
+ async fn send_bytes(&self, _data: Bytes) -> Result<()> {
84
+ Ok(())
85
+ }
86
+
87
+ async fn recv_bytes(&self) -> Result<Bytes> {
88
+ Err(crate::Error::StreamClosed)
89
+ }
90
+
91
+ async fn close_send(&self) -> Result<()> {
92
+ self.closed.store(true, Ordering::SeqCst);
93
+ Ok(())
94
+ }
95
+
96
+ async fn close(&self) -> Result<()> {
97
+ self.closed.store(true, Ordering::SeqCst);
98
+ Ok(())
99
+ }
100
+ }
101
+
102
+ #[async_trait]
103
+ impl RpcStream for MockRpcStream {
104
+ async fn send_packet(&self, packet: &RpcStreamPacket) -> Result<()> {
105
+ self.packets.lock().await.push_back(packet.clone());
106
+ Ok(())
107
+ }
108
+
109
+ async fn recv_packet(&self) -> Result<RpcStreamPacket> {
110
+ Err(crate::Error::StreamClosed)
111
+ }
112
+ }
113
+
114
+ #[tokio::test]
115
+ async fn test_rpc_stream_writer_write_packet() {
116
+ use std::sync::Arc;
117
+ let stream = Arc::new(MockRpcStream::new());
118
+ let writer = RpcStreamWriter::new(stream.clone());
119
+
120
+ let packet = crate::packet::new_call_start(
121
+ "test.Service".to_string(),
122
+ "TestMethod".to_string(),
123
+ Some(Bytes::from(vec![1, 2, 3])),
124
+ );
125
+
126
+ writer.write_packet(packet).await.unwrap();
127
+
128
+ let packets = stream.get_packets().await;
129
+ assert_eq!(packets.len(), 1);
130
+
131
+ match &packets[0].body {
132
+ Some(crate::rpcstream::RpcStreamPacketBody::Data(data)) => {
133
+ // Verify we can decode the inner packet
134
+ let inner = Packet::decode(&data[..]).unwrap();
135
+ assert!(inner.body.is_some());
136
+ }
137
+ _ => panic!("Expected Data packet"),
138
+ }
139
+ }
140
+
141
+ #[tokio::test]
142
+ async fn test_rpc_stream_writer_close() {
143
+ use std::sync::Arc;
144
+ let stream = Arc::new(MockRpcStream::new());
145
+ let writer = RpcStreamWriter::new(stream.clone());
146
+
147
+ writer.close().await.unwrap();
148
+ assert!(stream.closed.load(Ordering::SeqCst));
149
+ }
150
+ }
@@ -1,4 +1,4 @@
1
- // go:build deps_only
1
+ //go:build deps_only
2
2
 
3
3
  #include "server-rpc.hpp"
4
4
 
package/echo/build.rs DELETED
@@ -1,15 +0,0 @@
1
- use std::io::Result;
2
- use std::path::PathBuf;
3
-
4
- fn main() -> Result<()> {
5
- let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
6
- // Use simplified proto without rpcstream dependency
7
- let proto_path = manifest_dir.join("echo_rust.proto");
8
-
9
- println!("cargo:rerun-if-changed={}", proto_path.display());
10
-
11
- starpc_build::configure()
12
- .compile_protos(&[proto_path], &[&manifest_dir])?;
13
-
14
- Ok(())
15
- }
package/srpc/build.rs DELETED
@@ -1,15 +0,0 @@
1
- use std::io::Result;
2
- use std::path::PathBuf;
3
-
4
- fn main() -> Result<()> {
5
- // Get the path to the proto file in the same directory.
6
- let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
7
- let proto_path = manifest_dir.join("rpcproto.proto");
8
-
9
- println!("cargo:rerun-if-changed={}", proto_path.display());
10
-
11
- prost_build::Config::new()
12
- .compile_protos(&[proto_path], &[&manifest_dir])?;
13
-
14
- Ok(())
15
- }