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 up.
TargetUp {
/// ID of the target.
target_id: u16,
},
/// Worker -> Master target down.
TargetDown {
/// ID of the target.
target_id: u16,
},
/// Worker -> Master target unknown.
TargetUnknown {
/// ID of the target.
target_id: u16,
},
}
impl From<&MessagePayload> for u8 {
fn from(message: &MessagePayload) -> u8 {
match message {
MessagePayload::Authentication { .. } => 0,
MessagePayload::TargetUp { .. } => 1,
MessagePayload::TargetDown { .. } => 2,
MessagePayload::TargetUnknown { .. } => 3,
}
}
}
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::TargetUp { target_id } => {
buf.extend(target_id.to_be_bytes());
}
MessagePayload::TargetDown { target_id } => {
buf.extend(target_id.to_be_bytes());
}
MessagePayload::TargetUnknown { target_id } => {
buf.extend(target_id.to_be_bytes());
}
}
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 => MessagePayload::TargetUp {
target_id: buf.read_u16::<BigEndian>()?,
},
2 => MessagePayload::TargetDown {
target_id: buf.read_u16::<BigEndian>()?,
},
3 => MessagePayload::TargetUnknown {
target_id: buf.read_u16::<BigEndian>()?,
},
_ => return Err(Box::new(DecodeError::InvalidPayloadType(payload_type))),
};
Ok(payload)
}
}
#[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_target_up() {
let sent_payload = MessagePayload::TargetUp { target_id: 42 };
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);
}