use super::datagram::{HEADER_LENGTH, UdpDatagramMut};
use core::fmt;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum UdpDatagramBuildError {
MissingDestinationPort,
DatagramLengthTooLarge,
BufferTooShort {
required: usize,
available: usize,
},
}
impl fmt::Display for UdpDatagramBuildError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MissingDestinationPort => formatter.write_str("missing UDP destination port"),
Self::DatagramLengthTooLarge => {
formatter.write_str("UDP datagram length exceeds u16::MAX")
}
Self::BufferTooShort {
required,
available,
} => write!(
formatter,
"UDP buffer is too short: need {required} bytes, have {available}"
),
}
}
}
pub struct UdpDatagramBuilder<'a> {
buffer: &'a mut [u8],
payload_length: usize,
source_port: u16,
destination_port: Option<u16>,
checksum: u16,
}
impl<'a> UdpDatagramBuilder<'a> {
#[inline]
pub fn new(buffer: &'a mut [u8], payload_length: usize) -> Self {
Self {
buffer,
payload_length,
source_port: 0,
destination_port: None,
checksum: 0,
}
}
#[inline]
pub fn source_port(mut self, value: u16) -> Self {
self.source_port = value;
self
}
#[inline]
pub fn destination_port(mut self, value: u16) -> Self {
self.destination_port = Some(value);
self
}
#[inline]
pub fn checksum(mut self, value: u16) -> Self {
self.checksum = value;
self
}
pub fn build(self) -> Result<UdpDatagramMut<'a>, UdpDatagramBuildError> {
let destination_port = self
.destination_port
.ok_or(UdpDatagramBuildError::MissingDestinationPort)?;
let length = HEADER_LENGTH
.checked_add(self.payload_length)
.ok_or(UdpDatagramBuildError::DatagramLengthTooLarge)?;
let encoded_length =
u16::try_from(length).map_err(|_| UdpDatagramBuildError::DatagramLengthTooLarge)?;
if self.buffer.len() < length {
return Err(UdpDatagramBuildError::BufferTooShort {
required: length,
available: self.buffer.len(),
});
}
let bytes = &mut self.buffer[..length];
bytes[0..2].copy_from_slice(&self.source_port.to_be_bytes());
bytes[2..4].copy_from_slice(&destination_port.to_be_bytes());
bytes[4..6].copy_from_slice(&encoded_length.to_be_bytes());
bytes[6..8].copy_from_slice(&self.checksum.to_be_bytes());
Ok(UdpDatagramMut::from_validated(bytes))
}
}