use byteorder::{BigEndian, ReadBytesExt};
use crate::DecodeError;
/// Message Payload
///
/// Byte representation:
/// | [u8] |
/// | content bytes, structure varies per type |
#[derive(Debug, PartialEq)]
pub enum MessagePayload {
/// Master -> Worker authentication.
Authentication { key: String },
/// Master -> Worker target configuration.
/*ConfigureTarget {
target_id: u16,
method: TargetMethod,
addr: String
},*/
/// Worker -> Master target state change.
TargetStateChange {
/// ID of the target.
target_id: u16,
/// State the target changed to.
///
/// [`true`] means up.
/// [`false`] means down.
state: bool,
},
}
impl From<&MessagePayload> for u8 {
fn from(message: &MessagePayload) -> u8 {
match message {
MessagePayload::Authentication { .. } => 0,
MessagePayload::TargetStateChange { .. } => 1,
}
}
}
impl MessagePayload {
pub fn encode(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
match self {
MessagePayload::Authentication { key } => {
// Key length
buf.extend((key.len() as u32).to_be_bytes());
// Key
buf.extend(key.as_bytes());
}
MessagePayload::TargetStateChange { target_id, state } => {
buf.extend(target_id.to_be_bytes());
buf.push((*state).into());
}
}
buf
}
pub fn decode(
payload_type: u8,
buf: &mut impl std::io::Read,
) -> Result<MessagePayload, Box<dyn std::error::Error>> {
let payload = match payload_type {
0 => {
let key_length = buf.read_u32::<BigEndian>()?;
let mut key_bytes = vec![0; key_length as usize];
buf.read_exact(&mut key_bytes)?;
MessagePayload::Authentication {
key: String::from_utf8(key_bytes)?,
}
}
1 => {
let target_id = buf.read_u16::<BigEndian>()?;
let state = u8_to_bool(buf.read_u8()?)?;
MessagePayload::TargetStateChange { target_id, state }
}
_ => return Err(Box::new(DecodeError::InvalidPayloadType(payload_type))),
};
Ok(payload)
}
}
fn u8_to_bool(n: u8) -> Result<bool, DecodeError> {
match n {
0 => Ok(false),
1 => Ok(true),
_ => Err(DecodeError::InvalidBoolean(n)),
}
}
#[test]
fn test_round_trip_payload_authentication() {
let sent_payload = MessagePayload::Authentication {
key: "this_is_a_key".to_owned(),
};
let sent_payload_bytes = sent_payload.encode();
let sent_payload_type = (&sent_payload).into();
let mut reader = std::io::Cursor::new(sent_payload_bytes);
let received_payload = MessagePayload::decode(sent_payload_type, &mut reader).unwrap();
assert_eq!(sent_payload, received_payload);
}
#[test]
fn test_round_trip_payload_targetstatechange() {
let sent_payload = MessagePayload::TargetStateChange {
target_id: 42,
state: true,
};
let sent_payload_bytes = sent_payload.encode();
let sent_payload_type = (&sent_payload).into();
let mut reader = std::io::Cursor::new(sent_payload_bytes);
let received_payload = MessagePayload::decode(sent_payload_type, &mut reader).unwrap();
assert_eq!(sent_payload, received_payload);
}