const TAG: u8 = 0x5B;
pub struct InBandSignaling {
_private: (),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Signal {
SeedRotation,
ReKey,
TransportSwitch,
Shutdown,
Custom(Vec<u8>),
}
impl InBandSignaling {
pub fn new() -> Self {
Self { _private: () }
}
pub fn encode(&self, signal: &Signal) -> Vec<u8> {
match signal {
Signal::SeedRotation => vec![TAG, 1],
Signal::ReKey => vec![TAG, 2],
Signal::TransportSwitch => vec![TAG, 3],
Signal::Shutdown => vec![TAG, 4],
Signal::Custom(p) => {
let mut v = vec![TAG, 5];
let len = u16::try_from(p.len()).expect("custom signal payload exceeds u16::MAX");
v.extend_from_slice(&len.to_be_bytes());
v.extend_from_slice(p);
v
}
}
}
pub fn decode(&self, data: &[u8]) -> Option<Signal> {
if data.len() < 2 || data[0] != TAG {
return None;
}
match data[1] {
1 => Some(Signal::SeedRotation),
2 => Some(Signal::ReKey),
3 => Some(Signal::TransportSwitch),
4 => Some(Signal::Shutdown),
5 => {
if data.len() < 4 {
return None;
}
let len = u16::from_be_bytes([data[2], data[3]]) as usize;
if data.len() != 4 + len {
return None;
}
Some(Signal::Custom(data[4..].to_vec()))
}
_ => None,
}
}
}
impl Default for InBandSignaling {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_custom() {
let s = InBandSignaling::new();
let sig = Signal::Custom(b"ctl".to_vec());
let b = s.encode(&sig);
assert_eq!(s.decode(&b), Some(sig));
}
}