use packet_encoding::{decodable_enum, Decodable, Encodable};
use crate::{Error, Result};
#[derive(Debug, Clone, PartialEq)]
pub struct TxLabel(u8);
const MAX_TX_LABEL: u8 = 0xF;
impl TryFrom<u8> for TxLabel {
type Error = Error;
fn try_from(value: u8) -> Result<Self> {
if value > MAX_TX_LABEL {
Err(Error::OutOfRange)
} else {
Ok(TxLabel(value))
}
}
}
impl From<&TxLabel> for u8 {
fn from(v: &TxLabel) -> u8 {
v.0
}
}
impl From<&TxLabel> for usize {
fn from(v: &TxLabel) -> usize {
v.0 as usize
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ProfileId([u8; 2]);
pub(crate) const AV_REMOTE_PROFILE: &ProfileId = &ProfileId([0x11, 0x0e]);
impl From<[u8; 2]> for ProfileId {
fn from(value: [u8; 2]) -> Self {
Self(value)
}
}
decodable_enum! {
pub enum PacketType<u8, Error, OutOfRange> {
Single = 0x00,
Start = 0x01,
Continue = 0x02,
End = 0x03,
}
}
decodable_enum! {
pub enum MessageType<u8, Error, OutOfRange> {
Command = 0x00,
Response = 0x01,
}
}
#[derive(Debug)]
pub struct Header {
label: TxLabel, packet_type: PacketType, message_type: MessageType, invalid_profile_id: bool, num_packets: u8, profile_id: ProfileId, }
impl Header {
pub(crate) fn new(
label: TxLabel,
profile_id: ProfileId,
message_type: MessageType,
invalid_profile_id: bool,
) -> Header {
Header {
label,
profile_id,
message_type,
packet_type: PacketType::Single,
invalid_profile_id,
num_packets: 1,
}
}
pub(crate) fn create_response(&self, packet_type: PacketType) -> Header {
Header {
label: self.label.clone(),
profile_id: self.profile_id.clone(),
message_type: MessageType::Response,
packet_type,
invalid_profile_id: false,
num_packets: 1,
}
}
pub(crate) fn create_invalid_profile_id_response(&self) -> Header {
Header {
label: self.label.clone(),
profile_id: self.profile_id.clone(),
message_type: MessageType::Response,
packet_type: PacketType::Single,
invalid_profile_id: true,
num_packets: 1,
}
}
pub(crate) fn label(&self) -> &TxLabel {
&self.label
}
pub(crate) fn profile_id(&self) -> &ProfileId {
&self.profile_id
}
pub fn message_type(&self) -> &MessageType {
&self.message_type
}
pub fn packet_type(&self) -> &PacketType {
&self.packet_type
}
pub fn is_invalid_profile_id(&self) -> bool {
self.invalid_profile_id
}
pub fn is_type(&self, other: &MessageType) -> bool {
&self.message_type == other
}
pub fn is_single(&self) -> bool {
self.packet_type == PacketType::Single
}
}
impl Decodable for Header {
type Error = Error;
fn decode(bytes: &[u8]) -> Result<Header> {
if bytes.len() < 3 {
return Err(Error::OutOfRange);
}
let label = TxLabel::try_from(bytes[0] >> 4)?;
let packet_type = PacketType::try_from((bytes[0] >> 2) & 0x3)?;
let (id_offset, num_packets) = match packet_type {
PacketType::Start => {
if bytes.len() < 4 {
return Err(Error::OutOfRange);
}
(2, bytes[1])
}
_ => (1, 1),
};
let profile_id = ProfileId::from([bytes[id_offset], bytes[id_offset + 1]]);
let invalid_profile_id = bytes[0] & 0x1 == 1;
let header = Header {
label,
profile_id,
message_type: MessageType::try_from(bytes[0] >> 1 & 0x1)?,
packet_type,
invalid_profile_id,
num_packets,
};
Ok(header)
}
}
impl Encodable for Header {
type Error = Error;
fn encoded_len(&self) -> usize {
match self.packet_type {
PacketType::Start => 4,
_ => 3,
}
}
fn encode(&self, buf: &mut [u8]) -> Result<()> {
if buf.len() < self.encoded_len() {
return Err(Error::Encoding);
}
let invalid_profile_id: u8 = if self.invalid_profile_id { 1 } else { 0 };
buf[0] = u8::from(&self.label) << 4
| u8::from(&self.packet_type) << 2
| u8::from(&self.message_type) << 1
| invalid_profile_id;
let mut buf_idx = 1;
if self.packet_type == PacketType::Start {
buf[buf_idx] = self.num_packets;
buf_idx = 2;
}
let profile_id = self.profile_id.0;
buf[buf_idx] = profile_id[0];
buf[buf_idx + 1] = profile_id[1];
Ok(())
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_header_encode() {
let header =
Header::new(TxLabel(0), AV_REMOTE_PROFILE.clone(), MessageType::Command, false);
assert!(!header.is_invalid_profile_id());
assert!(header.is_single());
assert!(header.is_type(&MessageType::Command));
assert_eq!(TxLabel(0), *header.label());
let len = header.encoded_len();
assert_eq!(3, len);
let mut buf = vec![0; len];
assert!(header.encode(&mut buf[..]).is_ok());
assert_eq!(
&[
0x00, 0x11, 0x0e, ],
&buf[..]
);
}
#[test]
fn test_header_encode_response() {
let header =
Header::new(TxLabel(15), AV_REMOTE_PROFILE.clone(), MessageType::Command, false);
let header = header.create_response(PacketType::Single);
assert!(!header.is_invalid_profile_id());
assert!(header.is_single());
assert!(header.is_type(&MessageType::Response));
assert_eq!(TxLabel(15), *header.label());
let len = header.encoded_len();
assert_eq!(3, len);
let mut buf = vec![0; len];
assert!(header.encode(&mut buf[..]).is_ok());
assert_eq!(
&[
0xf2, 0x11, 0x0e, ],
&buf[..]
);
}
#[test]
fn test_header_encode_invalid_profile_response() {
let header =
Header::new(TxLabel(0), AV_REMOTE_PROFILE.clone(), MessageType::Command, false);
let header = header.create_invalid_profile_id_response();
assert!(header.is_invalid_profile_id());
assert!(header.is_single());
assert!(header.is_type(&MessageType::Response));
assert_eq!(TxLabel(0), *header.label());
let len = header.encoded_len();
assert_eq!(3, len);
let mut buf = vec![0; len];
assert!(header.encode(&mut buf[..]).is_ok());
assert_eq!(
&[
0x03, 0x11, 0x0e, ],
&buf[..]
);
}
#[test]
fn test_header_decode_invalid_packet_response() {
let header = Header::decode(&[
0xf3, 0x11, 0x0e, 0x12, 0x34, 0x45, ])
.expect("unable to decode header");
assert!(header.is_invalid_profile_id());
assert!(header.is_single());
assert!(header.is_type(&MessageType::Response));
assert_eq!(TxLabel(15), *header.label());
}
#[test]
fn test_header_decode_command() {
let header = Header::decode(&[
0x80, 0x11, 0x0e, 0x34, 0x45, ])
.expect("unable to decode header");
assert!(!header.is_invalid_profile_id());
assert!(header.is_single());
assert!(header.is_type(&MessageType::Command));
assert_eq!(TxLabel(8), *header.label());
}
#[test]
fn test_header_decode_invalid() {
assert_eq!(
Error::OutOfRange,
Header::decode(&[
0x80, 0x11, ])
.unwrap_err()
);
}
#[test]
fn txlabel_tofrom_u8() {
let mut label: Result<TxLabel> = TxLabel::try_from(15);
assert!(label.is_ok());
assert_eq!(15, u8::from(&label.unwrap()));
label = TxLabel::try_from(16);
assert_eq!(Err(Error::OutOfRange), label);
}
#[test]
fn txlabel_to_usize() {
let label = TxLabel::try_from(1).unwrap();
assert_eq!(1, usize::from(&label));
}
}