use crate::error::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Address(u8);
impl Address {
pub const fn from_raw(byte: u8) -> Self {
Self(byte)
}
pub const fn pd(addr: u8) -> Result<Self, Error> {
if addr > crate::BROADCAST_ADDR {
return Err(Error::BadAddress(addr));
}
Ok(Self(addr))
}
pub const fn reply(addr: u8) -> Result<Self, Error> {
if addr > crate::BROADCAST_ADDR {
return Err(Error::BadAddress(addr));
}
Ok(Self(addr | crate::REPLY_FLAG))
}
pub const fn as_byte(self) -> u8 {
self.0
}
pub const fn pd_addr(self) -> u8 {
self.0 & !crate::REPLY_FLAG
}
pub const fn is_reply(self) -> bool {
(self.0 & crate::REPLY_FLAG) != 0
}
pub const fn is_broadcast(self) -> bool {
self.pd_addr() == crate::BROADCAST_ADDR
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Sqn(u8);
impl Sqn {
pub const ZERO: Self = Self(0);
pub const fn new(n: u8) -> Result<Self, Error> {
if n > 3 {
Err(Error::BadSqn(n))
} else {
Ok(Self(n))
}
}
pub const fn value(self) -> u8 {
self.0
}
pub const fn next(self) -> Self {
Self(match self.0 {
0 | 3 => 1,
n => n + 1,
})
}
}
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct CtrlFlags: u8 {
const USE_CRC = 0x04;
const HAS_SCB = 0x08;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ControlByte {
pub sqn: Sqn,
pub flags: CtrlFlags,
}
impl ControlByte {
pub const fn new(sqn: Sqn, flags: CtrlFlags) -> Self {
Self { sqn, flags }
}
pub const fn encode(self) -> u8 {
self.sqn.value() | self.flags.bits()
}
pub const fn decode(byte: u8) -> Result<Self, Error> {
if (byte & 0xF0) != 0 {
return Err(Error::BadControlByte(byte));
}
let sqn = match Sqn::new(byte & 0x03) {
Ok(s) => s,
Err(e) => return Err(e),
};
let flags = match CtrlFlags::from_bits(byte & 0x0C) {
Some(f) => f,
None => return Err(Error::BadControlByte(byte)),
};
Ok(Self { sqn, flags })
}
pub const fn use_crc(self) -> bool {
self.flags.contains(CtrlFlags::USE_CRC)
}
pub const fn has_scb(self) -> bool {
self.flags.contains(CtrlFlags::HAS_SCB)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ctrl_byte_roundtrip() {
for raw in 0u8..=0x0F {
let cb = ControlByte::decode(raw).unwrap();
assert_eq!(cb.encode(), raw);
}
}
#[test]
fn ctrl_byte_rejects_reserved() {
for raw in 0x10..=0xFFu8 {
assert!(ControlByte::decode(raw).is_err());
}
}
#[test]
fn sqn_cycles_skipping_zero() {
let mut s = Sqn::new(1).unwrap();
for _ in 0..10 {
s = s.next();
assert_ne!(s.value(), 0);
}
}
#[test]
fn address_reply_flag() {
let pd = Address::pd(0x05).unwrap();
assert!(!pd.is_reply());
assert_eq!(pd.as_byte(), 0x05);
let reply = Address::reply(0x05).unwrap();
assert!(reply.is_reply());
assert_eq!(reply.as_byte(), 0x85);
assert_eq!(reply.pd_addr(), 0x05);
let bcast = Address::pd(0x7F).unwrap();
assert!(bcast.is_broadcast());
}
}