use core::fmt;
use crate::view::{DESTINATION, EXPECTING_REPLY, NETWORK_MESSAGE, SOURCE};
use crate::{Address, GLOBAL_NETWORK, MAX_MAC_LEN, Npdu, VERSION};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum EncodeError {
NoRoom {
needed: usize,
available: usize,
},
ReservedNetwork,
EmptySource,
AddressTooLong,
}
impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NoRoom { needed, available } => {
write!(f, "an NPDU of {needed} octets doesn't fit in {available}")
}
Self::ReservedNetwork => f.write_str("NPDU names a reserved network number"),
Self::EmptySource => f.write_str("NPDU source SLEN=0 is invalid"),
Self::AddressTooLong => write!(f, "NPDU address exceeds the {MAX_MAC_LEN}-octet limit"),
}
}
}
impl core::error::Error for EncodeError {}
impl Npdu<'_> {
#[must_use]
pub fn encoded_len(&self) -> usize {
let address = |address: Option<Address<'_>>| address.map_or(0, |a| 3 + a.mac.len());
let message = match self.message_type {
Some(kind) if kind.is_proprietary() => 3,
Some(_) => 1,
None => 0,
};
2 + address(self.destination)
+ address(self.source)
+ usize::from(self.destination.is_some())
+ message
+ self.payload.len()
}
pub fn encode_into(&self, out: &mut [u8]) -> Result<usize, EncodeError> {
if let Some(destination) = self.destination {
if destination.network == 0 {
return Err(EncodeError::ReservedNetwork);
}
if destination.mac.len() > MAX_MAC_LEN {
return Err(EncodeError::AddressTooLong);
}
}
if let Some(source) = self.source {
if source.network == 0 || source.network == GLOBAL_NETWORK {
return Err(EncodeError::ReservedNetwork);
}
if source.mac.is_empty() {
return Err(EncodeError::EmptySource);
}
if source.mac.len() > MAX_MAC_LEN {
return Err(EncodeError::AddressTooLong);
}
}
let needed = self.encoded_len();
let available = out.len();
let out = out
.get_mut(..needed)
.ok_or(EncodeError::NoRoom { needed, available })?;
let mut control = self.priority.to_raw();
if self.message_type.is_some() {
control |= NETWORK_MESSAGE;
}
if self.destination.is_some() {
control |= DESTINATION;
}
if self.source.is_some() {
control |= SOURCE;
}
if self.expecting_reply {
control |= EXPECTING_REPLY;
}
let mut cursor = Cursor { out, at: 0 };
cursor.put(&[VERSION, control]);
if let Some(destination) = self.destination {
cursor.put_address(destination);
}
if let Some(source) = self.source {
cursor.put_address(source);
}
if self.destination.is_some() {
cursor.put(&[self.hop_count]);
}
if let Some(kind) = self.message_type {
cursor.put(&[kind.to_raw()]);
if kind.is_proprietary() {
cursor.put(&self.vendor_id.unwrap_or(0).to_be_bytes());
}
}
cursor.put(self.payload);
debug_assert_eq!(cursor.at, needed);
Ok(needed)
}
}
struct Cursor<'o> {
out: &'o mut [u8],
at: usize,
}
impl Cursor<'_> {
fn put(&mut self, octets: &[u8]) {
self.out[self.at..self.at + octets.len()].copy_from_slice(octets);
self.at += octets.len();
}
fn put_address(&mut self, address: Address<'_>) {
self.put(&address.network.to_be_bytes());
self.put(&[address.mac.len() as u8]);
self.put(address.mac);
}
}