1use crate::{Admission, Crdt, EventLog, Record, RecordId, Replica, VersionVector};
6use alloc::{collections::BTreeSet, vec::Vec};
7
8#[derive(Clone, Debug, PartialEq, Eq)]
9pub struct TransportEnvelope<D> {
10 pub from: u64,
11 pub to: u64,
12 pub records: Vec<Record<D>>,
13}
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum TransportError {
17 NotSubscribed { peer: u64 },
18 Disconnected { from: u64, to: u64 },
19}
20
21impl core::fmt::Display for TransportError {
22 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
23 match self {
24 Self::NotSubscribed { peer } => write!(f, "peer {peer} is not subscribed"),
25 Self::Disconnected { from, to } => {
26 write!(f, "transport link from {from} to {to} is disconnected")
27 }
28 }
29 }
30}
31
32impl core::error::Error for TransportError {}
33
34pub trait TransportAdapter<D> {
40 fn subscribe(&mut self, peer: u64);
41 fn is_subscribed(&self, peer: u64) -> bool;
42 fn set_connected(&mut self, a: u64, b: u64, connected: bool);
43 fn is_connected(&self, a: u64, b: u64) -> bool;
44 fn send(&mut self, from: u64, to: u64, records: Vec<Record<D>>) -> Result<(), TransportError>;
45 fn drain(&mut self, peer: u64) -> Vec<TransportEnvelope<D>>;
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
56pub struct InMemoryTransport<D> {
57 subscribed: BTreeSet<u64>,
58 disconnected: BTreeSet<(u64, u64)>,
59 queue: Vec<TransportEnvelope<D>>,
60 dropped: Vec<TransportEnvelope<D>>,
61 drop_next: bool,
62 duplicate_next: bool,
63}
64
65impl<D> InMemoryTransport<D> {
66 pub fn new() -> Self {
67 InMemoryTransport {
68 subscribed: BTreeSet::new(),
69 disconnected: BTreeSet::new(),
70 queue: Vec::new(),
71 dropped: Vec::new(),
72 drop_next: false,
73 duplicate_next: false,
74 }
75 }
76
77 pub fn drop_next_send(&mut self) {
78 self.drop_next = true;
79 }
80
81 pub fn duplicate_next_send(&mut self) {
82 self.duplicate_next = true;
83 }
84
85 pub fn reverse_pending_for(&mut self, peer: u64) {
86 let mut peer_items = Vec::new();
87 let mut retained = Vec::new();
88 for envelope in self.queue.drain(..) {
89 if envelope.to == peer {
90 peer_items.push(envelope);
91 } else {
92 retained.push(envelope);
93 }
94 }
95 peer_items.reverse();
96 retained.extend(peer_items);
97 self.queue = retained;
98 }
99
100 pub fn pending_len(&self) -> usize {
101 self.queue.len()
102 }
103
104 pub fn dropped_len(&self) -> usize {
105 self.dropped.len()
106 }
107
108 pub fn drain_dropped(&mut self) -> usize {
110 let count = self.dropped_len();
111 self.clear_dropped();
112 count
113 }
114
115 pub fn clear_dropped(&mut self) {
117 self.dropped.clear();
118 }
119
120 fn link(a: u64, b: u64) -> (u64, u64) {
121 if a <= b {
122 (a, b)
123 } else {
124 (b, a)
125 }
126 }
127}
128
129impl<D> Default for InMemoryTransport<D> {
130 fn default() -> Self {
131 Self::new()
132 }
133}
134
135impl<D: Clone> TransportAdapter<D> for InMemoryTransport<D> {
136 fn subscribe(&mut self, peer: u64) {
137 self.subscribed.insert(peer);
138 }
139
140 fn is_subscribed(&self, peer: u64) -> bool {
141 self.subscribed.contains(&peer)
142 }
143
144 fn set_connected(&mut self, a: u64, b: u64, connected: bool) {
145 let link = Self::link(a, b);
146 if connected {
147 self.disconnected.remove(&link);
148 } else {
149 self.disconnected.insert(link);
150 }
151 }
152
153 fn is_connected(&self, a: u64, b: u64) -> bool {
154 !self.disconnected.contains(&Self::link(a, b))
155 }
156
157 fn send(&mut self, from: u64, to: u64, records: Vec<Record<D>>) -> Result<(), TransportError> {
158 if !self.is_subscribed(from) {
159 return Err(TransportError::NotSubscribed { peer: from });
160 }
161 if !self.is_subscribed(to) {
162 return Err(TransportError::NotSubscribed { peer: to });
163 }
164 if !self.is_connected(from, to) {
165 return Err(TransportError::Disconnected { from, to });
166 }
167
168 let envelope = TransportEnvelope { from, to, records };
169 if self.drop_next {
170 self.drop_next = false;
171 self.dropped.push(envelope);
172 return Ok(());
173 }
174
175 self.queue.push(envelope.clone());
176 if self.duplicate_next {
177 self.duplicate_next = false;
178 self.queue.push(envelope);
179 }
180 Ok(())
181 }
182
183 fn drain(&mut self, peer: u64) -> Vec<TransportEnvelope<D>> {
184 let mut incoming = Vec::new();
185 let mut retained = Vec::new();
186 for envelope in self.queue.drain(..) {
187 let connected = !self
188 .disconnected
189 .contains(&Self::link(envelope.from, envelope.to));
190 if envelope.to == peer && connected {
191 incoming.push(envelope);
192 } else {
193 retained.push(envelope);
194 }
195 }
196 self.queue = retained;
197 incoming
198 }
199}
200
201pub fn queue_anti_entropy<D, T>(
204 transport: &mut T,
205 from: u64,
206 to: u64,
207 local: &EventLog<D>,
208 remote_version: &VersionVector,
209) -> Result<(), TransportError>
210where
211 D: Clone,
212 T: TransportAdapter<D>,
213{
214 let records = local.since(remote_version);
215 if records.is_empty() {
216 Ok(())
217 } else {
218 transport.send(from, to, records)
219 }
220}
221
222#[must_use = "inspect sync admissions for Invalid and Collision refusals"]
237pub fn anti_entropy<C, T>(
238 transport: &mut T,
239 from: u64,
240 to: u64,
241 local: &EventLog<C::Delta>,
242 remote: &mut Replica<C>,
243) -> Result<Vec<(u64, RecordId, Admission)>, TransportError>
244where
245 C: Crdt,
246 C::Delta: Clone + PartialEq,
247 T: TransportAdapter<C::Delta>,
248{
249 queue_anti_entropy(transport, from, to, local, remote.version())?;
250 let mut admissions = Vec::new();
251 for envelope in transport.drain(to) {
252 for record in envelope.records {
253 let id = record.id;
254 admissions.push((envelope.from, id, remote.admit(record)));
255 }
256 }
257 Ok(admissions)
258}