tauri_plugin_py_runtime/bridge/
transport.rs1use bytes::Bytes;
9use futures_util::SinkExt;
10use tokio::{io::AsyncWrite, sync::mpsc};
11use tokio_util::codec::FramedWrite;
12
13use crate::{
14 bridge::protocol::{length_codec, Frame},
15 error::{codes, Error, Result},
16};
17
18#[derive(Clone)]
23pub struct Writer {
24 tx: mpsc::UnboundedSender<Bytes>,
25 max_frame_size: usize,
26}
27
28impl Writer {
29 pub fn channel(max_frame_size: usize) -> (Self, mpsc::UnboundedReceiver<Bytes>) {
33 let (tx, rx) = mpsc::unbounded_channel();
34 (Self { tx, max_frame_size }, rx)
35 }
36
37 pub fn spawn<W: AsyncWrite + Unpin + Send + 'static>(stream: W, max_frame_size: usize) -> Self {
41 let (writer, mut rx) = Self::channel(max_frame_size);
42 tauri::async_runtime::spawn(async move {
43 let mut sink = FramedWrite::new(stream, length_codec(max_frame_size));
44 while let Some(frame) = rx.recv().await {
45 if let Err(e) = sink.send(frame).await {
46 log::error!(target: "py-runtime", "failed to write frame: {e}");
47 break;
48 }
49 }
50 });
51 writer
52 }
53
54 pub fn send(&self, frame: Frame) -> Result<()> {
61 let data = frame.encode();
62 if data.len() > self.max_frame_size {
63 return Err(Error::new(
64 codes::FRAME_TOO_LARGE,
65 format!("frame of {} bytes exceeds limit of {}", data.len(), self.max_frame_size),
66 ));
67 }
68 self
69 .tx
70 .send(data)
71 .map_err(|_| Error::new(codes::RUNTIME_UNAVAILABLE, "python runtime is not running"))
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use crate::bridge::protocol::{FrameCodec, Message, DEFAULT_MAX_FRAME_SIZE};
79 use futures_util::StreamExt;
80 use tokio_util::codec::FramedRead;
81
82 fn close(channel: u64) -> Frame {
83 Frame::new(Message::ChannelClose { channel })
84 }
85
86 #[tokio::test]
87 async fn spawned_writer_writes_frames_in_order() {
88 let (a, b) = tokio::io::duplex(1024);
89 let writer = Writer::spawn(a, DEFAULT_MAX_FRAME_SIZE);
90 let mut frames = FramedRead::new(b, FrameCodec::new(DEFAULT_MAX_FRAME_SIZE));
91 for i in 0..3 {
92 writer.send(close(i)).unwrap();
93 }
94 for i in 0..3 {
95 assert_eq!(frames.next().await.unwrap().unwrap().unwrap(), close(i));
96 }
97 }
98
99 #[test]
100 fn rejects_oversized_frames() {
101 let (writer, _rx) = Writer::channel(16);
102 let mut frame = close(1);
103 frame.body = vec![0; 100];
104 assert_eq!(writer.send(frame).unwrap_err().code, "frame_too_large");
105 }
106
107 #[test]
108 fn send_after_close_is_unavailable() {
109 let (writer, rx) = Writer::channel(1024);
110 drop(rx);
111 assert_eq!(writer.send(close(1)).unwrap_err().code, "runtime_unavailable");
112 }
113}