mod codes;
use core::fmt;
pub use codes::{MessageType, RejectReason};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum DataError {
Length {
length: usize,
},
NoRoom {
needed: usize,
available: usize,
},
TooManyEntries {
count: usize,
},
}
impl fmt::Display for DataError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Length { length } => {
write!(f, "{length} octets of message data don't fit its layout")
}
Self::NoRoom { needed, available } => {
write!(
f,
"message data of {needed} octets doesn't fit in {available}"
)
}
Self::TooManyEntries { count } => {
write!(f, "{count} routing entries, more than a count can state")
}
}
}
}
impl core::error::Error for DataError {}
const fn bad_length(payload: &[u8]) -> DataError {
DataError::Length {
length: payload.len(),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Networks<'a> {
octets: &'a [u8],
}
impl<'a> Networks<'a> {
pub const EMPTY: Networks<'static> = Networks { octets: &[] };
pub const fn parse(payload: &'a [u8]) -> Result<Self, DataError> {
if payload.len().is_multiple_of(2) {
Ok(Self { octets: payload })
} else {
Err(bad_length(payload))
}
}
#[must_use]
pub const fn len(&self) -> usize {
self.octets.len() / 2
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.octets.is_empty()
}
#[must_use]
pub const fn as_bytes(&self) -> &'a [u8] {
self.octets
}
#[must_use]
pub fn contains(&self, network: u16) -> bool {
self.iter().any(|listed| listed == network)
}
pub fn iter(&self) -> impl Iterator<Item = u16> + 'a {
self.octets
.as_chunks::<2>()
.0
.iter()
.map(|pair| u16::from_be_bytes(*pair))
}
pub fn write<I>(networks: I, out: &mut [u8]) -> Result<usize, DataError>
where
I: IntoIterator<Item = u16>,
I::IntoIter: Clone,
{
let networks = networks.into_iter();
let available = out.len();
let needed = networks.clone().count().saturating_mul(2);
let out = out
.get_mut(..needed)
.ok_or(DataError::NoRoom { needed, available })?;
for (pair, network) in out.as_chunks_mut::<2>().0.iter_mut().zip(networks) {
*pair = network.to_be_bytes();
}
Ok(needed)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WhoIsRouter {
All,
Network(u16),
}
impl WhoIsRouter {
pub const fn parse(payload: &[u8]) -> Result<Self, DataError> {
match *payload {
[] => Ok(Self::All),
[high, low] => Ok(Self::Network(u16::from_be_bytes([high, low]))),
_ => Err(bad_length(payload)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Reject {
pub reason: RejectReason,
pub network: u16,
}
impl Reject {
pub const fn parse(payload: &[u8]) -> Result<Self, DataError> {
match *payload {
[reason, high, low] => Ok(Self {
reason: RejectReason::from_raw(reason),
network: u16::from_be_bytes([high, low]),
}),
_ => Err(bad_length(payload)),
}
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 3] {
let [high, low] = self.network.to_be_bytes();
[self.reason.to_raw(), high, low]
}
}
const fn network_and_octet(payload: &[u8]) -> Result<(u16, u8), DataError> {
match *payload {
[high, low, octet] => Ok((u16::from_be_bytes([high, low]), octet)),
_ => Err(bad_length(payload)),
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ICouldBeRouter {
pub network: u16,
pub performance_index: u8,
}
impl ICouldBeRouter {
pub const fn parse(payload: &[u8]) -> Result<Self, DataError> {
match network_and_octet(payload) {
Ok((network, performance_index)) => Ok(Self {
network,
performance_index,
}),
Err(error) => Err(error),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EstablishConnection {
pub network: u16,
pub termination_time: u8,
}
impl EstablishConnection {
pub const fn parse(payload: &[u8]) -> Result<Self, DataError> {
match network_and_octet(payload) {
Ok((network, termination_time)) => Ok(Self {
network,
termination_time,
}),
Err(error) => Err(error),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DisconnectConnection {
pub network: u16,
}
impl DisconnectConnection {
pub const fn parse(payload: &[u8]) -> Result<Self, DataError> {
match *payload {
[high, low] => Ok(Self {
network: u16::from_be_bytes([high, low]),
}),
_ => Err(bad_length(payload)),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NetworkNumberIs {
pub network: u16,
pub flag: u8,
}
impl NetworkNumberIs {
pub const CONFIGURED: u8 = 1;
pub const fn parse(payload: &[u8]) -> Result<Self, DataError> {
match network_and_octet(payload) {
Ok((network, flag)) => Ok(Self { network, flag }),
Err(error) => Err(error),
}
}
#[must_use]
pub const fn is_configured(self) -> bool {
self.flag == Self::CONFIGURED
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 3] {
let [high, low] = self.network.to_be_bytes();
[high, low, self.flag]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RoutingEntries<'a> {
count: u8,
entries: &'a [u8],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RoutingEntry<'a> {
pub network: u16,
pub port_id: u8,
pub port_info: &'a [u8],
}
impl<'a> RoutingEntries<'a> {
pub const ENTRY_LEN: usize = 4;
pub fn parse(payload: &'a [u8]) -> Result<Self, DataError> {
let (&count, entries) = payload.split_first().ok_or(bad_length(payload))?;
let mut rest = entries;
for _ in 0..count {
rest = split_entry(rest).ok_or(bad_length(payload))?.1;
}
if !rest.is_empty() {
return Err(bad_length(payload));
}
Ok(Self { count, entries })
}
#[must_use]
pub const fn count(&self) -> u8 {
self.count
}
pub fn iter(&self) -> impl Iterator<Item = RoutingEntry<'a>> + 'a {
let mut rest = self.entries;
core::iter::from_fn(move || {
let (entry, after) = split_entry(rest)?;
rest = after;
Some(entry)
})
}
pub fn write<I>(entries: I, out: &mut [u8]) -> Result<usize, DataError>
where
I: IntoIterator<Item = (u16, u8)>,
I::IntoIter: Clone,
{
let entries = entries.into_iter();
let count = entries.clone().count();
let count = u8::try_from(count).map_err(|_| DataError::TooManyEntries { count })?;
let needed = 1 + usize::from(count) * Self::ENTRY_LEN;
let available = out.len();
let room = out
.get_mut(..needed)
.ok_or(DataError::NoRoom { needed, available })?;
let (first, rest) = room
.split_first_mut()
.ok_or(DataError::NoRoom { needed, available })?;
*first = count;
for (slot, (network, port_id)) in rest.as_chunks_mut::<4>().0.iter_mut().zip(entries) {
let [high, low] = network.to_be_bytes();
*slot = [high, low, port_id, 0];
}
Ok(needed)
}
}
fn split_entry(octets: &[u8]) -> Option<(RoutingEntry<'_>, &[u8])> {
let [high, low, port_id, info_len, ref rest @ ..] = *octets else {
return None;
};
let port_info = rest.get(..usize::from(info_len))?;
let entry = RoutingEntry {
network: u16::from_be_bytes([high, low]),
port_id,
port_info,
};
Some((entry, &rest[port_info.len()..]))
}