1use core::time::Duration;
8
9use buggy::Bug;
10use heapless::Vec;
11use postcard::Error as PostcardError;
12use serde::{Deserialize, Serialize};
13
14use crate::{
15 Address, MaxCut, Prior,
16 command::{CmdId, Command, Priority},
17 storage::{GraphId, MAX_COMMAND_LENGTH, StorageError},
18};
19
20mod requester;
21mod responder;
22mod wire;
23
24use requester::SyncRequestMessage;
25pub use requester::SyncRequester;
26use responder::SyncResponseMessage;
27pub use responder::{PeerCache, SyncResponder};
28use wire::{SubscribeResult, SyncHelloType, SyncType};
29
30pub const PEER_HEAD_MAX: usize = 10;
34
35#[cfg(feature = "low-mem-usage")]
37const COMMAND_SAMPLE_MAX: usize = 20;
38#[cfg(not(feature = "low-mem-usage"))]
39const COMMAND_SAMPLE_MAX: usize = 100;
40
41#[cfg(feature = "low-mem-usage")]
44const REQUEST_MISSING_MAX: usize = 1;
45#[cfg(not(feature = "low-mem-usage"))]
46const REQUEST_MISSING_MAX: usize = 100;
47
48#[cfg(feature = "low-mem-usage")]
50pub const COMMAND_RESPONSE_MAX: usize = 5;
51#[cfg(not(feature = "low-mem-usage"))]
52pub const COMMAND_RESPONSE_MAX: usize = 100;
53
54#[cfg(feature = "low-mem-usage")]
56const SEGMENT_BUFFER_MAX: usize = 10;
57#[cfg(not(feature = "low-mem-usage"))]
58const SEGMENT_BUFFER_MAX: usize = 100;
59
60pub const MAX_SYNC_MESSAGE_SIZE: usize = 1024 + MAX_COMMAND_LENGTH * COMMAND_RESPONSE_MAX;
64
65#[derive(Debug, thiserror::Error)]
67#[non_exhaustive]
68pub enum SyncError {
69 #[error("sync session ID does not match")]
70 SessionMismatch,
71 #[error("missing sync response")]
72 MissingSyncResponse,
73 #[error("syncer state not valid for this message")]
74 SessionState,
75 #[error("syncer not ready for operation")]
76 NotReady,
77 #[error("too many commands sent")]
78 CommandOverflow,
79 #[error("storage error: {0}")]
80 Storage(#[from] StorageError),
81 #[error("serialize error: {0}")]
82 Serialize(#[from] PostcardError),
83 #[error(transparent)]
84 Bug(#[from] Bug),
85}
86
87#[derive(Serialize, Deserialize, Debug)]
89pub struct SyncCommand<'a> {
90 priority: Priority,
91 id: CmdId,
92 parent: Prior<Address>,
93 policy: Option<&'a [u8]>,
94 data: &'a [u8],
95 max_cut: MaxCut,
96}
97
98impl<'a> Command for SyncCommand<'a> {
99 fn priority(&self) -> Priority {
100 self.priority.clone()
101 }
102
103 fn id(&self) -> CmdId {
104 self.id
105 }
106
107 fn parent(&self) -> Prior<Address> {
108 self.parent
109 }
110
111 fn policy(&self) -> Option<&'a [u8]> {
112 self.policy
113 }
114
115 fn bytes(&self) -> &'a [u8] {
116 self.data
117 }
118
119 fn max_cut(&self) -> Result<MaxCut, Bug> {
120 Ok(self.max_cut)
121 }
122}
123
124#[allow(clippy::large_enum_variant)]
132pub enum SyncIncoming<'a> {
133 Poll(PollIncoming),
135 Subscribe(SubscribeIncoming),
137 Unsubscribe(UnsubscribeIncoming),
139 Push(PushIncoming<'a>),
141 Hello(SyncHello),
143}
144
145impl<'a> SyncIncoming<'a> {
146 pub fn decode(data: &'a [u8]) -> Result<Self, SyncError> {
148 let (sync_type, remaining) = postcard::take_from_bytes::<SyncType>(data)?;
149 Ok(match sync_type {
150 SyncType::Poll { request } => Self::Poll(PollIncoming {
151 session_id: request.session_id(),
152 message: request,
153 }),
154 SyncType::Subscribe {
155 remain_open,
156 max_bytes,
157 commands,
158 graph_id,
159 } => Self::Subscribe(SubscribeIncoming {
160 graph_id,
161 remain_open,
162 max_bytes,
163 heads: SyncHeads { inner: commands },
164 }),
165 SyncType::Unsubscribe { graph_id } => {
166 Self::Unsubscribe(UnsubscribeIncoming { graph_id })
167 }
168 SyncType::Push { message, graph_id } => Self::Push(PushIncoming {
169 graph_id,
170 session_id: message.session_id(),
171 message,
172 command_data: remaining,
173 }),
174 SyncType::Hello(hello) => Self::Hello(hello.into()),
175 })
176 }
177}
178
179pub struct SyncHeads {
181 inner: Vec<Address, COMMAND_SAMPLE_MAX>,
182}
183
184impl SyncHeads {
185 pub fn as_slice(&self) -> &[Address] {
187 &self.inner
188 }
189
190 pub fn iter(&self) -> impl DoubleEndedIterator<Item = Address> + ExactSizeIterator + '_ {
192 self.inner.iter().copied()
193 }
194}
195
196#[derive(Debug)]
198pub enum SyncHello {
199 Subscribe(HelloSubscribe),
201 Unsubscribe(HelloUnsubscribe),
203 Hello(HelloNotification),
205}
206
207impl From<SyncHelloType> for SyncHello {
208 fn from(t: SyncHelloType) -> Self {
209 match t {
210 SyncHelloType::Subscribe {
211 graph_id,
212 graph_change_delay,
213 duration,
214 schedule_delay,
215 } => Self::Subscribe(HelloSubscribe {
216 graph_id,
217 graph_change_delay,
218 duration,
219 schedule_delay,
220 }),
221 SyncHelloType::Unsubscribe { graph_id } => {
222 Self::Unsubscribe(HelloUnsubscribe { graph_id })
223 }
224 SyncHelloType::Hello { graph_id, head } => {
225 Self::Hello(HelloNotification { graph_id, head })
226 }
227 }
228 }
229}
230
231#[derive(Debug)]
233pub struct HelloSubscribe {
234 graph_id: GraphId,
235 graph_change_delay: Duration,
236 duration: Duration,
237 schedule_delay: Duration,
238}
239
240impl HelloSubscribe {
241 pub fn graph_id(&self) -> GraphId {
243 self.graph_id
244 }
245
246 pub fn graph_change_delay(&self) -> Duration {
248 self.graph_change_delay
249 }
250
251 pub fn duration(&self) -> Duration {
253 self.duration
254 }
255
256 pub fn schedule_delay(&self) -> Duration {
258 self.schedule_delay
259 }
260}
261
262#[derive(Debug)]
264pub struct HelloUnsubscribe {
265 graph_id: GraphId,
266}
267
268impl HelloUnsubscribe {
269 pub fn graph_id(&self) -> GraphId {
271 self.graph_id
272 }
273}
274
275#[derive(Debug)]
277pub struct HelloNotification {
278 graph_id: GraphId,
279 head: Address,
280}
281
282impl HelloNotification {
283 pub fn graph_id(&self) -> GraphId {
285 self.graph_id
286 }
287
288 pub fn head(&self) -> Address {
290 self.head
291 }
292}
293
294pub struct PollIncoming {
297 session_id: u128,
298 pub(crate) message: SyncRequestMessage,
299}
300
301impl PollIncoming {
302 pub fn session_id(&self) -> u128 {
304 self.session_id
305 }
306}
307
308pub struct SubscribeIncoming {
310 graph_id: GraphId,
311 remain_open: u64,
312 max_bytes: u64,
313 heads: SyncHeads,
314}
315
316impl SubscribeIncoming {
317 pub fn graph_id(&self) -> GraphId {
319 self.graph_id
320 }
321
322 pub fn remain_open(&self) -> Duration {
324 Duration::from_secs(self.remain_open)
325 }
326
327 pub fn max_bytes(&self) -> u64 {
329 self.max_bytes
330 }
331
332 pub fn heads(&self) -> &SyncHeads {
334 &self.heads
335 }
336}
337
338pub struct UnsubscribeIncoming {
340 graph_id: GraphId,
341}
342
343impl UnsubscribeIncoming {
344 pub fn graph_id(&self) -> GraphId {
346 self.graph_id
347 }
348}
349
350pub struct PushIncoming<'a> {
353 graph_id: GraphId,
354 session_id: u128,
355 pub(crate) message: SyncResponseMessage,
356 pub(crate) command_data: &'a [u8],
357}
358
359impl PushIncoming<'_> {
360 pub fn graph_id(&self) -> GraphId {
362 self.graph_id
363 }
364
365 pub fn session_id(&self) -> u128 {
367 self.session_id
368 }
369}
370
371#[derive(Debug)]
374pub enum SubscribeResponse {
375 Success,
377 TooManySubscriptions,
379}
380
381impl SubscribeResponse {
382 pub fn encode_to(self, target: &mut [u8]) -> Result<usize, SyncError> {
384 let inner = match self {
385 Self::Success => SubscribeResult::Success,
386 Self::TooManySubscriptions => SubscribeResult::TooManySubscriptions,
387 };
388 Ok(postcard::to_slice(&inner, target)?.len())
389 }
390
391 pub fn decode(data: &[u8]) -> Result<Self, SyncError> {
393 let inner: SubscribeResult = postcard::from_bytes(data)?;
394 Ok(match inner {
395 SubscribeResult::Success => Self::Success,
396 SubscribeResult::TooManySubscriptions => Self::TooManySubscriptions,
397 })
398 }
399}