Skip to main content

nodedb_cluster/
sync_frame_versioned.rs

1// SPDX-License-Identifier: BUSL-1.1
2
3//! Versioned encode/decode wrappers for [`nodedb_types::SyncFrame`].
4//!
5//! `SyncFrame` lives in `nodedb-types` and cannot reference `nodedb-cluster`,
6//! so versioning wrappers for the sync wire layer are provided here instead.
7//!
8//! # PhysicalPlan bridge wire exclusion
9//!
10//! The `bridge::physical_plan::wire` codec is NOT versioned here. It operates
11//! on the Control⟷Data SPSC boundary (within a single node, never across the
12//! network) and is already round-trip tested. Cross-cluster boundary versioning
13//! does not apply to it.
14
15use nodedb_types::SyncFrame;
16
17use crate::error::{ClusterError, Result};
18use crate::wire_version::{unwrap_bytes_versioned, wrap_bytes_versioned};
19
20/// Encode a [`SyncFrame`] and wrap its bytes in a v2 versioned envelope.
21///
22/// The inner bytes are `SyncFrame::to_bytes()` output. The outer envelope
23/// lets the receiving peer detect and reject incompatible future versions
24/// rather than silently misinterpreting the frame.
25pub fn encode_versioned_sync_frame(frame: &SyncFrame) -> Result<Vec<u8>> {
26    let raw = frame.to_bytes();
27    wrap_bytes_versioned(&raw).map_err(|e| ClusterError::Codec {
28        detail: format!("SyncFrame versioned encode: {e}"),
29    })
30}
31
32/// Decode a versioned-envelope-wrapped [`SyncFrame`].
33///
34/// Requires a v2 versioned envelope; rejects bytes without the envelope marker
35/// and envelopes with unsupported future version numbers.
36pub fn decode_versioned_sync_frame(data: &[u8]) -> Result<SyncFrame> {
37    let inner = unwrap_bytes_versioned(data).map_err(|e| ClusterError::Codec {
38        detail: format!("SyncFrame versioned decode: {e}"),
39    })?;
40    SyncFrame::from_bytes(inner).ok_or_else(|| ClusterError::Codec {
41        detail: "SyncFrame::from_bytes failed: malformed or truncated frame".to_string(),
42    })
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use nodedb_types::{SyncFrame, SyncMessageType};
49
50    // A minimal zerompk-serializable struct to fill a SyncFrame body.
51    #[derive(
52        serde::Serialize, serde::Deserialize, zerompk::ToMessagePack, zerompk::FromMessagePack,
53    )]
54    struct PingMsg {
55        seq: u64,
56    }
57
58    fn make_ping_frame() -> SyncFrame {
59        let msg = PingMsg { seq: 1 };
60        SyncFrame::new_msgpack(SyncMessageType::PingPong, &msg).unwrap()
61    }
62
63    #[test]
64    fn sync_frame_versioned_roundtrip() {
65        let frame = make_ping_frame();
66        let encoded = encode_versioned_sync_frame(&frame).unwrap();
67        let decoded = decode_versioned_sync_frame(&encoded).unwrap();
68        assert_eq!(frame.msg_type, decoded.msg_type);
69        assert_eq!(frame.body, decoded.body);
70    }
71}