Skip to main content

vox_types/
link.rs

1#![allow(async_fn_in_trait)]
2
3use std::future::Future;
4
5use crate::Backing;
6
7/// Requested conduit mode for the transport prologue.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum TransportMode {
10    Bare,
11    Stable,
12}
13
14/// Marker trait that requires [`Send`] on native targets, nothing on wasm32.
15#[cfg(not(target_arch = "wasm32"))]
16pub trait MaybeSend: Send {}
17#[cfg(not(target_arch = "wasm32"))]
18impl<T: Send> MaybeSend for T {}
19
20#[cfg(target_arch = "wasm32")]
21pub trait MaybeSend {}
22#[cfg(target_arch = "wasm32")]
23impl<T> MaybeSend for T {}
24
25/// Marker trait that requires [`Sync`] on native targets, nothing on wasm32.
26#[cfg(not(target_arch = "wasm32"))]
27pub trait MaybeSync: Sync {}
28#[cfg(not(target_arch = "wasm32"))]
29impl<T: Sync> MaybeSync for T {}
30
31#[cfg(target_arch = "wasm32")]
32pub trait MaybeSync {}
33#[cfg(target_arch = "wasm32")]
34impl<T> MaybeSync for T {}
35
36/// A future that is `Send` on native targets, nothing on wasm32.
37/// Unlike `MaybeSend`, this can be used as `dyn MaybeSendFuture` because
38/// it's a single trait (not `dyn Future + MaybeSend`).
39#[cfg(not(target_arch = "wasm32"))]
40pub trait MaybeSendFuture: Future + Send {}
41#[cfg(not(target_arch = "wasm32"))]
42impl<T: Future + Send> MaybeSendFuture for T {}
43
44#[cfg(target_arch = "wasm32")]
45pub trait MaybeSendFuture: Future {}
46#[cfg(target_arch = "wasm32")]
47impl<T: Future> MaybeSendFuture for T {}
48
49/// Bidirectional raw-bytes transport.
50///
51/// TCP, WebSocket, SHM all implement this. No knowledge of what's being
52/// sent — just bytes in, bytes out. The transport provides write buffers
53/// so callers can encode directly into the destination (zero-copy for SHM).
54// r[impl link]
55// r[impl link.message]
56// r[impl link.order]
57pub trait Link {
58    type Tx: LinkTx;
59    type Rx: LinkRx;
60
61    // r[impl link.split]
62    fn split(self) -> (Self::Tx, Self::Rx);
63
64    /// Whether this link supports the requested transport mode.
65    ///
66    /// Most links support both `bare` and `stable`. Special transports may
67    /// override this to reject unsupported modes during the transport
68    /// prologue.
69    fn supports_transport_mode(mode: TransportMode) -> bool
70    where
71        Self: Sized,
72    {
73        let _ = mode;
74        true
75    }
76}
77
78/// A permit for allocating exactly one outbound payload.
79///
80/// Returned by [`LinkTx::reserve`]. The permit represents *message-level*
81/// capacity (not bytes). Once you have a permit, turning it into a concrete
82/// buffer for a specific payload size is synchronous.
83// r[impl link.tx.permit.drop]
84pub trait LinkTxPermit {
85    type Slot: WriteSlot;
86
87    /// Allocate a writable buffer of exactly `len` bytes.
88    ///
89    /// This is synchronous once the permit has been acquired.
90    // r[impl link.tx.alloc.limits]
91    // r[impl link.message.empty]
92    fn alloc(self, len: usize) -> std::io::Result<Self::Slot>;
93}
94
95/// Sending half of a [`Link`].
96///
97/// Uses a two-phase write:
98///
99/// 1. [`reserve`](LinkTx::reserve) awaits until the transport can accept *one*
100///    more payload and returns a [`LinkTxPermit`].
101/// 2. [`LinkTxPermit::alloc`] allocates a [`WriteSlot`] backed by the
102///    transport's own buffer (bipbuffer slot, kernel write buffer, etc.),
103///    then the caller fills it and calls [`WriteSlot::commit`].
104///
105/// `reserve` is the backpressure point.
106pub trait LinkTx: MaybeSend + MaybeSync + 'static {
107    type Permit: LinkTxPermit + MaybeSend;
108
109    /// Reserve capacity to send exactly one payload.
110    ///
111    /// Backpressure lives here — it awaits until the transport can accept a
112    /// payload (or errors).
113    ///
114    /// Dropping the returned permit without allocating/committing MUST
115    /// release the reservation.
116    // r[impl link.tx.reserve]
117    // r[impl link.tx.cancel-safe]
118    fn reserve(&self) -> impl Future<Output = std::io::Result<Self::Permit>> + MaybeSend + '_;
119
120    /// Graceful close of the outbound direction.
121    // r[impl link.tx.close]
122    fn close(self) -> impl Future<Output = std::io::Result<()>> + MaybeSend
123    where
124        Self: Sized;
125}
126
127/// A writable slot in the transport's output buffer.
128///
129/// Obtained from [`LinkTxPermit::alloc`]. The caller writes encoded bytes into
130/// [`as_mut_slice`](WriteSlot::as_mut_slice), then calls
131/// [`commit`](WriteSlot::commit) to make them visible to the receiver.
132///
133/// Dropping without commit = discard (no bytes sent, space reclaimed).
134// r[impl link.tx.discard]
135// r[impl zerocopy.framing.link]
136pub trait WriteSlot {
137    /// The writable buffer, exactly the size requested in `alloc`.
138    // r[impl link.tx.slot.len]
139    fn as_mut_slice(&mut self) -> &mut [u8];
140
141    /// Commit the written bytes. After this, the receiver can see them.
142    /// Sync — the bytes are already in the transport's buffer.
143    // r[impl link.tx.commit]
144    fn commit(self);
145}
146
147/// Receiving half of a [`Link`].
148///
149/// Yields [`Backing`] values: the raw bytes plus their ownership handle.
150/// The transport handles framing (length-prefix, WebSocket frames, etc.)
151/// and returns exactly one message's bytes per `recv` call.
152///
153/// For SHM: the Backing might be a VarSlot reference.
154/// For TCP: the Backing is a heap-allocated buffer.
155pub trait LinkRx: MaybeSend + 'static {
156    type Error: std::error::Error + MaybeSend + MaybeSync + 'static;
157
158    /// Receive the next message's raw bytes.
159    ///
160    /// Returns `Ok(None)` when the peer has closed the connection.
161    // r[impl link.rx.recv]
162    // r[impl link.rx.error]
163    // r[impl link.rx.eof]
164    fn recv(
165        &mut self,
166    ) -> impl Future<Output = Result<Option<Backing>, Self::Error>> + MaybeSend + '_;
167}
168
169/// A [`Link`] assembled from pre-split Tx and Rx halves.
170pub struct SplitLink<Tx, Rx> {
171    pub tx: Tx,
172    pub rx: Rx,
173}
174
175impl<Tx: LinkTx, Rx: LinkRx> Link for SplitLink<Tx, Rx> {
176    type Tx = Tx;
177    type Rx = Rx;
178
179    fn split(self) -> (Tx, Rx) {
180        (self.tx, self.rx)
181    }
182}