use anyhow::{anyhow, Result};
use bytes::{Buf, BufMut, Bytes, BytesMut};
pub const KIND_MESSAGE: &str = "messaging.msg";
pub const KIND_ACK: &str = "messaging.ack";
pub fn encode_message(message_id: [u8; 16], timestamp_ms: u64, ciphertext: &[u8]) -> Bytes {
let mut out = BytesMut::with_capacity(16 + 8 + ciphertext.len());
out.put_slice(&message_id);
out.put_u64(timestamp_ms);
out.put_slice(ciphertext);
out.freeze()
}
pub fn decode_message(body: &[u8]) -> Result<DecodedMessage> {
if body.len() < 24 {
return Err(anyhow!(
"messaging.msg too short: need ≥ 24, have {}",
body.len()
));
}
let mut message_id = [0u8; 16];
message_id.copy_from_slice(&body[..16]);
let timestamp_ms = (&body[16..24]).get_u64();
let ciphertext = body[24..].to_vec();
Ok(DecodedMessage {
message_id,
timestamp_ms,
ciphertext,
})
}
pub fn encode_ack(message_id: [u8; 16], ack_kind: u8) -> Bytes {
let mut out = BytesMut::with_capacity(17);
out.put_slice(&message_id);
out.put_u8(ack_kind);
out.freeze()
}
pub fn decode_ack(body: &[u8]) -> Result<DecodedAck> {
if body.len() < 17 {
return Err(anyhow!(
"messaging.ack too short: need ≥ 17, have {}",
body.len()
));
}
let mut message_id = [0u8; 16];
message_id.copy_from_slice(&body[..16]);
let ack_kind = body[16];
Ok(DecodedAck {
message_id,
ack_kind,
})
}
#[derive(Debug, Clone)]
pub struct DecodedMessage {
pub message_id: [u8; 16],
pub timestamp_ms: u64,
pub ciphertext: Vec<u8>,
}
#[derive(Debug, Clone, Copy)]
pub struct DecodedAck {
pub message_id: [u8; 16],
pub ack_kind: u8,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn message_round_trip() {
let id = [0x55; 16];
let body = encode_message(id, 1_745_960_000_000, &[0xDE, 0xAD, 0xBE, 0xEF]);
let decoded = decode_message(&body).unwrap();
assert_eq!(decoded.message_id, id);
assert_eq!(decoded.timestamp_ms, 1_745_960_000_000);
assert_eq!(decoded.ciphertext, vec![0xDE, 0xAD, 0xBE, 0xEF]);
}
#[test]
fn ack_round_trip() {
let id = [0x77; 16];
let body = encode_ack(id, 2);
let decoded = decode_ack(&body).unwrap();
assert_eq!(decoded.message_id, id);
assert_eq!(decoded.ack_kind, 2);
}
#[test]
fn reject_truncated_message() {
let body = vec![0u8; 10];
assert!(decode_message(&body).is_err());
}
#[test]
fn reject_truncated_ack() {
let body = vec![0u8; 5];
assert!(decode_ack(&body).is_err());
}
}