Skip to main content

rns_embedded_core/
transport.rs

1use crate::{packet::PacketFrame, EmbeddedError, EmbeddedResult};
2use alloc::{collections::VecDeque, vec::Vec};
3
4#[derive(Debug, Clone, Copy, Eq, PartialEq)]
5pub enum LinkState {
6    Down,
7    Connecting,
8    Up,
9}
10
11#[derive(Debug, Clone, Copy, Eq, PartialEq)]
12pub struct TransportCaps {
13    pub mtu_hint: u16,
14    pub ordered_delivery: bool,
15}
16
17pub trait EmbeddedTransport {
18    fn link_state(&self) -> LinkState;
19    fn capabilities(&self) -> TransportCaps;
20    fn send_frame(&mut self, frame: &PacketFrame) -> EmbeddedResult<()>;
21    fn poll_frame(&mut self) -> EmbeddedResult<Option<PacketFrame>>;
22}
23
24#[derive(Debug, Clone, Copy, Eq, PartialEq)]
25pub enum FaultMode {
26    None,
27    DropEvery(usize),
28    DuplicateEvery(usize),
29    ReorderPairEvery(usize),
30    BackpressureEvery(usize),
31}
32
33pub struct FaultInjectingMockTransport {
34    caps: TransportCaps,
35    state: LinkState,
36    sent_count: usize,
37    recv_count: usize,
38    outbound: VecDeque<PacketFrame>,
39    inbound: VecDeque<PacketFrame>,
40    faults: Vec<FaultMode>,
41}
42
43impl FaultInjectingMockTransport {
44    pub fn new(caps: TransportCaps) -> Self {
45        Self {
46            caps,
47            state: LinkState::Up,
48            sent_count: 0,
49            recv_count: 0,
50            outbound: VecDeque::new(),
51            inbound: VecDeque::new(),
52            faults: Vec::new(),
53        }
54    }
55
56    pub fn with_faults(mut self, faults: Vec<FaultMode>) -> Self {
57        self.faults = faults;
58        self
59    }
60
61    pub fn set_state(&mut self, state: LinkState) {
62        self.state = state;
63    }
64
65    pub fn enqueue_inbound(&mut self, frames: impl IntoIterator<Item = PacketFrame>) {
66        self.inbound.extend(frames);
67    }
68
69    pub fn drain_outbound(&mut self) -> Vec<PacketFrame> {
70        self.outbound.drain(..).collect()
71    }
72
73    fn should_trigger(step: usize, n: usize) -> bool {
74        n > 0 && step > 0 && step % n == 0
75    }
76}
77
78impl EmbeddedTransport for FaultInjectingMockTransport {
79    fn link_state(&self) -> LinkState {
80        self.state
81    }
82
83    fn capabilities(&self) -> TransportCaps {
84        self.caps
85    }
86
87    fn send_frame(&mut self, frame: &PacketFrame) -> EmbeddedResult<()> {
88        if self.state != LinkState::Up {
89            return Err(EmbeddedError::Disconnected);
90        }
91        if frame.payload.len() > usize::from(self.caps.mtu_hint) {
92            return Err(EmbeddedError::InvalidArgument);
93        }
94
95        self.sent_count = self.sent_count.saturating_add(1);
96        for mode in &self.faults {
97            match *mode {
98                FaultMode::BackpressureEvery(n) if Self::should_trigger(self.sent_count, n) => {
99                    return Err(EmbeddedError::Backpressure);
100                }
101                FaultMode::DropEvery(n) if Self::should_trigger(self.sent_count, n) => {
102                    return Ok(());
103                }
104                FaultMode::DuplicateEvery(n) if Self::should_trigger(self.sent_count, n) => {
105                    self.outbound.push_back(frame.clone());
106                    self.outbound.push_back(frame.clone());
107                    return Ok(());
108                }
109                FaultMode::ReorderPairEvery(n) if Self::should_trigger(self.sent_count, n) => {
110                    if let Some(prev) = self.outbound.pop_back() {
111                        self.outbound.push_back(frame.clone());
112                        self.outbound.push_back(prev);
113                    } else {
114                        self.outbound.push_back(frame.clone());
115                    }
116                    return Ok(());
117                }
118                _ => {}
119            }
120        }
121
122        self.outbound.push_back(frame.clone());
123        Ok(())
124    }
125
126    fn poll_frame(&mut self) -> EmbeddedResult<Option<PacketFrame>> {
127        if self.state == LinkState::Down {
128            return Err(EmbeddedError::Disconnected);
129        }
130        self.recv_count = self.recv_count.saturating_add(1);
131        Ok(self.inbound.pop_front())
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::{
138        EmbeddedTransport, FaultInjectingMockTransport, FaultMode, LinkState, TransportCaps,
139    };
140    use crate::{packet::PacketFrame, EmbeddedError};
141
142    fn frame(kind: u8, seq: u32, payload: &[u8]) -> PacketFrame {
143        PacketFrame::new(kind, seq, payload.to_vec()).expect("frame")
144    }
145
146    #[test]
147    fn drop_every_n_discards_target_frame() {
148        let caps = TransportCaps { mtu_hint: 64, ordered_delivery: false };
149        let mut tx =
150            FaultInjectingMockTransport::new(caps).with_faults(vec![FaultMode::DropEvery(2)]);
151        tx.send_frame(&frame(1, 1, b"aa")).expect("send1");
152        tx.send_frame(&frame(1, 2, b"bb")).expect("send2");
153        tx.send_frame(&frame(1, 3, b"cc")).expect("send3");
154
155        let out = tx.drain_outbound();
156        assert_eq!(out.len(), 2);
157        assert_eq!(out[0].sequence, 1);
158        assert_eq!(out[1].sequence, 3);
159    }
160
161    #[test]
162    fn duplicate_every_n_emits_duplicate_frame() {
163        let caps = TransportCaps { mtu_hint: 64, ordered_delivery: false };
164        let mut tx =
165            FaultInjectingMockTransport::new(caps).with_faults(vec![FaultMode::DuplicateEvery(2)]);
166        tx.send_frame(&frame(1, 1, b"aa")).expect("send1");
167        tx.send_frame(&frame(1, 2, b"bb")).expect("send2");
168
169        let out = tx.drain_outbound();
170        assert_eq!(out.len(), 3);
171        assert_eq!(out[0].sequence, 1);
172        assert_eq!(out[1].sequence, 2);
173        assert_eq!(out[2].sequence, 2);
174    }
175
176    #[test]
177    fn reorder_pair_swaps_adjacent_frames_on_trigger() {
178        let caps = TransportCaps { mtu_hint: 64, ordered_delivery: false };
179        let mut tx = FaultInjectingMockTransport::new(caps)
180            .with_faults(vec![FaultMode::ReorderPairEvery(2)]);
181        tx.send_frame(&frame(1, 1, b"aa")).expect("send1");
182        tx.send_frame(&frame(1, 2, b"bb")).expect("send2");
183
184        let out = tx.drain_outbound();
185        assert_eq!(out.len(), 2);
186        assert_eq!(out[0].sequence, 2);
187        assert_eq!(out[1].sequence, 1);
188    }
189
190    #[test]
191    fn backpressure_every_n_maps_to_error() {
192        let caps = TransportCaps { mtu_hint: 64, ordered_delivery: false };
193        let mut tx = FaultInjectingMockTransport::new(caps)
194            .with_faults(vec![FaultMode::BackpressureEvery(2)]);
195        tx.send_frame(&frame(1, 1, b"aa")).expect("send1");
196        let err = tx.send_frame(&frame(1, 2, b"bb")).expect_err("backpressure");
197        assert_eq!(err, EmbeddedError::Backpressure);
198    }
199
200    #[test]
201    fn disconnected_state_rejects_send_and_poll() {
202        let caps = TransportCaps { mtu_hint: 64, ordered_delivery: false };
203        let mut tx = FaultInjectingMockTransport::new(caps);
204        tx.set_state(LinkState::Down);
205        let err = tx.send_frame(&frame(1, 1, b"aa")).expect_err("send disconnected");
206        assert_eq!(err, EmbeddedError::Disconnected);
207
208        let err = tx.poll_frame().expect_err("poll disconnected");
209        assert_eq!(err, EmbeddedError::Disconnected);
210    }
211
212    #[test]
213    fn mtu_violation_maps_to_invalid_argument() {
214        let caps = TransportCaps { mtu_hint: 4, ordered_delivery: false };
215        let mut tx = FaultInjectingMockTransport::new(caps);
216        let err = tx.send_frame(&frame(1, 1, b"payload-too-large")).expect_err("mtu violation");
217        assert_eq!(err, EmbeddedError::InvalidArgument);
218    }
219}