Skip to main content

cu29_logstream/
stream.rs

1//! Transport-independent, packet-oriented stream resource contracts.
2//!
3//! Data and feedback use the same transport guarantees: complete packets with
4//! verified integrity. Delivery, ordering, and uniqueness are not guaranteed.
5//! UDP supplies packet integrity through the network stack. Raw serial supplies
6//! neither packet boundaries nor integrity; the framing adapter between LogStream
7//! and serial adds and verifies a checksum and discards damaged frames.
8
9use core::fmt::Debug;
10
11/// Failure to submit one complete logstream packet.
12#[derive(Clone, Copy, Debug, PartialEq, Eq)]
13pub enum CuStreamTxError {
14    /// The bounded transport cannot accept this packet immediately.
15    WouldBlock,
16    /// The transport has failed and cannot accept the packet.
17    Failed(&'static str),
18}
19
20/// Failure to receive one complete logstream packet.
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum CuStreamRxError {
23    /// The next packet does not fit in the caller-provided buffer.
24    BufferTooSmall { needed: usize },
25    /// The transport has failed and cannot receive packets.
26    Failed(&'static str),
27}
28
29/// One-way, fire-and-forget transmission of complete logstream packets.
30///
31/// Implementations must return immediately without waiting, locking, allocating,
32/// retrying, acknowledging, or accepting a partial packet. `Ok(())` means only
33/// that the resource accepted this packet once; it does not guarantee delivery.
34///
35/// Carriers must protect complete packet boundaries and integrity. Packet hardware
36/// or the network stack may supply the checksum; adapters over byte streams must
37/// add framing, an integrity check, and receive resynchronization. The common
38/// LogStream envelope has no checksum. Carrier overhead is outside the packet MTU.
39pub trait CuStreamTx: Debug + Send + Sync {
40    /// Advance an accepted packet with bounded, nonblocking work. Returns true
41    /// while bytes remain. Drivers must keep polling until it returns false.
42    /// The default implementation reports an idle transport.
43    fn poll_pending(&mut self) -> core::result::Result<bool, CuStreamTxError> {
44        Ok(false)
45    }
46
47    fn try_send(&mut self, packet: &[u8]) -> core::result::Result<(), CuStreamTxError>;
48}
49
50impl<T: CuStreamTx + ?Sized> CuStreamTx for alloc::boxed::Box<T> {
51    fn poll_pending(&mut self) -> core::result::Result<bool, CuStreamTxError> {
52        (**self).poll_pending()
53    }
54    #[inline]
55    fn try_send(&mut self, packet: &[u8]) -> core::result::Result<(), CuStreamTxError> {
56        (**self).try_send(packet)
57    }
58}
59
60/// Nonblocking reception of complete logstream packets.
61///
62/// `Ok(None)` means no complete packet is available. `Ok(Some(len))` places one
63/// complete packet in `packet[..len]`. This is the receive half of the one-way
64/// data plane; it does not acknowledge traffic or imply a reverse channel.
65///
66/// Only deliver complete packets that passed the carrier's integrity check. Drop
67/// corrupt, truncated, or malformed frames before exposing them to FEC; corruption
68/// must become packet loss, which FEC can recover. Never deliver a partial packet.
69/// Byte-stream adapters must verify and strip their framing checksum and
70/// resynchronize after damaged frames. In-memory carriers must preserve bytes.
71/// Record digests bind reconstructed content and recovery references separately;
72/// they do not replace this carrier integrity contract.
73pub trait CuStreamRx: Debug + Send + Sync {
74    fn try_recv(
75        &mut self,
76        packet: &mut [u8],
77    ) -> core::result::Result<Option<usize>, CuStreamRxError>;
78}
79
80impl<T: CuStreamRx + ?Sized> CuStreamRx for alloc::boxed::Box<T> {
81    #[inline]
82    fn try_recv(
83        &mut self,
84        packet: &mut [u8],
85    ) -> core::result::Result<Option<usize>, CuStreamRxError> {
86        (**self).try_recv(packet)
87    }
88}
89
90/// Optional advisory receive direction. This logical interface may share the
91/// physical stream carrier. A shared endpoint must have one receive owner and
92/// route complete packet kinds; cloned competing readers cannot provide routing.
93/// Protocol decoding, capability negotiation, and feedback policy belong above it.
94///
95/// The complete-packet integrity contract of [`CuStreamRx`] applies here too.
96/// Discard damaged or truncated frames in the transport adapter before delivery.
97/// Reports may be lost, duplicated, or reordered; report sequencing belongs to
98/// the feedback controller.
99pub trait CuFeedbackRx: Debug + Send + Sync {
100    fn try_recv_feedback(
101        &mut self,
102        packet: &mut [u8],
103    ) -> core::result::Result<Option<usize>, CuStreamRxError>;
104}
105
106/// Optional advisory transmit direction; same atomic, bounded, complete-packet
107/// integrity contract as [`CuStreamTx`]. Acceptance guarantees neither delivery
108/// nor ordering. A raw byte stream needs a framing and integrity adapter.
109pub trait CuFeedbackTx: Debug + Send + Sync {
110    fn try_send_feedback(&mut self, packet: &[u8]) -> core::result::Result<(), CuStreamTxError>;
111}
112
113/// Static autonomous wiring. There is no feedback polling or capability discovery.
114#[derive(Debug)]
115pub struct OneWay<T> {
116    tx: T,
117}
118impl<T> OneWay<T> {
119    pub const fn new(tx: T) -> Self {
120        Self { tx }
121    }
122    pub fn into_inner(self) -> T {
123        self.tx
124    }
125}
126impl<T: CuStreamTx> CuStreamTx for OneWay<T> {
127    fn poll_pending(&mut self) -> core::result::Result<bool, CuStreamTxError> {
128        self.tx.poll_pending()
129    }
130
131    fn try_send(&mut self, packet: &[u8]) -> core::result::Result<(), CuStreamTxError> {
132        self.tx.try_send(packet)
133    }
134}
135impl<T: CuStreamTx> CuFeedbackRx for OneWay<T> {
136    fn try_recv_feedback(
137        &mut self,
138        _packet: &mut [u8],
139    ) -> core::result::Result<Option<usize>, CuStreamRxError> {
140        Ok(None)
141    }
142}
143
144/// Explicit feedback wiring for separate logical endpoints. The endpoints may be
145/// resource handles backed by one carrier; physical out-of-band links are optional.
146#[derive(Debug)]
147pub struct SeparateFeedback<T, R> {
148    pub tx: T,
149    pub feedback_rx: R,
150}
151impl<T: CuStreamTx, R: CuFeedbackRx> CuStreamTx for SeparateFeedback<T, R> {
152    fn poll_pending(&mut self) -> core::result::Result<bool, CuStreamTxError> {
153        self.tx.poll_pending()
154    }
155
156    fn try_send(&mut self, packet: &[u8]) -> core::result::Result<(), CuStreamTxError> {
157        self.tx.try_send(packet)
158    }
159}
160impl<T: CuStreamTx, R: CuFeedbackRx> CuFeedbackRx for SeparateFeedback<T, R> {
161    fn try_recv_feedback(
162        &mut self,
163        packet: &mut [u8],
164    ) -> core::result::Result<Option<usize>, CuStreamRxError> {
165        self.feedback_rx.try_recv_feedback(packet)
166    }
167}