wrpc_transport/frame/
oneshot.rs1use bytes::Bytes;
4use tokio::io::{
5 AsyncRead, AsyncWrite, BufReader, DuplexStream, ReadHalf, WriteHalf, duplex, split,
6};
7use tracing::instrument;
8
9use crate::Invoke;
10use crate::frame::{Incoming, Outgoing, invoke};
11
12#[derive(Debug)]
17pub struct Oneshot<I, O>(std::sync::Mutex<Option<(I, O)>>);
18
19impl<I, O> From<(I, O)> for Oneshot<I, O> {
20 fn from((rx, tx): (I, O)) -> Self {
21 Self(std::sync::Mutex::new(Some((rx, tx))))
22 }
23}
24
25impl From<DuplexStream> for Oneshot<ReadHalf<DuplexStream>, WriteHalf<DuplexStream>> {
26 fn from(stream: DuplexStream) -> Self {
27 split(stream).into()
28 }
29}
30
31impl Oneshot<ReadHalf<DuplexStream>, WriteHalf<DuplexStream>> {
32 #[must_use]
34 pub fn duplex(max_buf_size: usize) -> (Self, DuplexStream) {
35 let (clt, srv) = duplex(max_buf_size);
36 (clt.into(), srv)
37 }
38}
39
40impl<I, O> Oneshot<I, O> {
41 pub fn try_take_inner(&self) -> std::io::Result<(I, O)> {
43 match self.0.try_lock().map(|mut stream| stream.take()) {
44 Ok(Some((rx, tx))) => Ok((rx, tx)),
45 Ok(None) | Err(std::sync::TryLockError::WouldBlock) => Err(std::io::Error::new(
46 std::io::ErrorKind::UnexpectedEof,
47 "stream was already used",
48 )),
49 Err(std::sync::TryLockError::Poisoned(..)) => {
50 Err(std::io::Error::other("stream lock poisoned"))
51 }
52 }
53 }
54}
55
56impl<I, O> Invoke for Oneshot<I, O>
57where
58 I: AsyncRead + Send + Unpin + 'static,
59 O: AsyncWrite + Send + Unpin + 'static,
60{
61 type Context = ();
62
63 async fn invoke<P>(
64 &self,
65 cx: Self::Context,
66 instance: &str,
67 func: &str,
68 params: Bytes,
69 paths: impl AsRef<[P]> + Send,
70 ) -> anyhow::Result<(Outgoing, Incoming)>
71 where
72 P: AsRef<[Option<usize>]> + Send + Sync,
73 {
74 (&self).invoke(cx, instance, func, params, paths).await
75 }
76}
77
78impl<I, O> Invoke for &Oneshot<I, O>
79where
80 I: AsyncRead + Send + Unpin + 'static,
81 O: AsyncWrite + Send + Unpin + 'static,
82{
83 type Context = ();
84
85 #[instrument(level = "trace", skip(self, paths, params), fields(params = format!("{params:02x?}")))]
86 fn invoke<P>(
87 &self,
88 (): Self::Context,
89 instance: &str,
90 func: &str,
91 params: Bytes,
92 paths: impl AsRef<[P]> + Send,
93 ) -> impl Future<Output = anyhow::Result<(Outgoing, Incoming)>>
94 where
95 P: AsRef<[Option<usize>]> + Send + Sync,
96 {
97 let stream = self.try_take_inner();
98 async move {
99 let (rx, tx) = stream?;
100 invoke(tx, BufReader::new(rx), instance, func, params, paths).await
101 }
102 }
103}