Skip to main content

gsp/
signal.rs

1//! GSP signal codec. Six CBOR keys.
2
3use gbp::CodecError;
4use serde::{Deserialize, Serialize};
5use serde_bytes::ByteBuf;
6
7/// GSP signal envelope. `args` carries opcode-specific CBOR bytes.
8#[derive(Clone, Debug, Serialize, Deserialize)]
9pub struct GspSignal {
10    /// `SignalType` widened to `u32` for CBOR uint compatibility.
11    #[serde(rename = "t")]
12    pub signal_type: u32,
13    /// Request identifier (echoed in ACK / NACK and used for deduplication).
14    #[serde(rename = "rid")]
15    pub request_id: u32,
16    /// Sender member id.
17    #[serde(rename = "sid")]
18    pub sender_id: u32,
19    /// Role claim (used by `ROLE_CHANGE`).
20    #[serde(rename = "rc")]
21    pub role_claim: u32,
22    /// Declared length of [`args`](Self::args).
23    #[serde(rename = "alen")]
24    pub args_length: u32,
25    /// Opcode-specific CBOR-encoded arguments.
26    #[serde(rename = "args")]
27    pub args: ByteBuf,
28}
29
30impl GspSignal {
31    /// Builds a signal with no arguments.
32    pub fn bare(signal_type: u32, request_id: u32, sender_id: u32) -> Self {
33        Self {
34            signal_type,
35            request_id,
36            sender_id,
37            role_claim: 0,
38            args_length: 0,
39            args: ByteBuf::new(),
40        }
41    }
42
43    /// CBOR-encodes the signal.
44    pub fn to_cbor(&self) -> Vec<u8> {
45        let mut buf = Vec::new();
46        ciborium::into_writer(self, &mut buf).expect("cbor encode");
47        buf
48    }
49
50    /// Decodes a CBOR-encoded signal and validates `args_length`.
51    pub fn from_cbor(data: &[u8]) -> Result<Self, CodecError> {
52        let s: Self = ciborium::from_reader(data).map_err(|e| CodecError::Decode(e.to_string()))?;
53        if s.args_length as usize != s.args.len() {
54            return Err(CodecError::PayloadSizeMismatch);
55        }
56        Ok(s)
57    }
58}