delta_pack/sync_session.rs
1use crate::DeltaPack;
2
3/// A stateful handle over a one-way state-sync session between two endpoints.
4///
5/// Each endpoint holds a [`SyncSession`] representing the current shared view
6/// of the peer's state. The sender calls [`encode`](Self::encode) to produce
7/// bytes; the receiver calls [`decode`](Self::decode) to apply them. Both
8/// sides converge on the same view as long as messages are delivered in order.
9///
10/// The type handles the "first call is full encode, subsequent are diffs"
11/// distinction internally. It also ensures the sender's view always matches
12/// what the peer reconstructs — even when the user's `state` has been mutated
13/// or reordered in ways that would break a raw `encode_diff` call.
14///
15/// # Example
16///
17/// ```ignore
18/// let mut session = SyncSession::<GameState>::new();
19///
20/// // Sender
21/// let bytes = session.encode(&state);
22///
23/// // Receiver
24/// let state = session.decode(&bytes);
25/// ```
26pub struct SyncSession<T: DeltaPack + Clone> {
27 view: Option<T>,
28}
29
30impl<T: DeltaPack + Clone> SyncSession<T> {
31 /// Create a new session with no view yet.
32 pub fn new() -> Self {
33 Self { view: None }
34 }
35
36 /// Produce bytes to send to the peer. First call emits a full encode;
37 /// subsequent calls emit a diff against the current view. Either way,
38 /// the internal view is updated to match what the peer will hold after
39 /// applying the returned bytes.
40 pub fn encode(&mut self, state: &T) -> Vec<u8> {
41 match &self.view {
42 None => {
43 let bytes = state.encode();
44 // Full encode iterates `state` in its insertion order, so
45 // `state`'s order is the wire order. A clone captures a safe
46 // "peer view" copy.
47 self.view = Some(state.clone());
48 bytes
49 }
50 Some(view) => {
51 let bytes = T::encode_diff(view, state);
52 // Simulate the peer's decode so our view stays aligned with
53 // theirs, regardless of any reordering in the user's `state`.
54 self.view = Some(T::decode_diff(view, &bytes));
55 bytes
56 }
57 }
58 }
59
60 /// Apply bytes received from the peer. First call expects a full encode;
61 /// subsequent calls expect a diff. Returns a reference to the updated view.
62 pub fn decode(&mut self, bytes: &[u8]) -> &T {
63 self.view = Some(match &self.view {
64 None => T::decode(bytes),
65 Some(view) => T::decode_diff(view, bytes),
66 });
67 self.view.as_ref().expect("view just assigned")
68 }
69
70 /// The current view, or `None` if neither [`encode`](Self::encode) nor
71 /// [`decode`](Self::decode) has been called.
72 pub fn current(&self) -> Option<&T> {
73 self.view.as_ref()
74 }
75}
76
77impl<T: DeltaPack + Clone> Default for SyncSession<T> {
78 fn default() -> Self {
79 Self::new()
80 }
81}