use core::fmt::Display;
use std::io;
use std::net::UdpSocket;
use std::sync::{Arc, Mutex};
use std::time::Duration;
#[derive(Debug, Clone, Copy)]
pub enum UdpMode {
Udpin,
Udpout,
UdpBroadcast,
}
#[derive(Debug, Clone)]
pub struct UdpConfig {
pub(crate) address: String,
pub(crate) mode: UdpMode,
pub(crate) read_timeout: Option<Duration>,
pub(crate) source: UdpSource,
}
#[derive(Debug, Clone)]
pub(crate) enum UdpSource {
Address,
Socket(Arc<Mutex<Option<UdpSocket>>>),
}
impl UdpConfig {
pub fn new(address: String, mode: UdpMode) -> Self {
Self {
address,
mode,
read_timeout: None,
source: UdpSource::Address,
}
}
pub fn from_socket(socket: UdpSocket, mode: UdpMode) -> io::Result<Self> {
let address = match mode {
UdpMode::Udpin => socket.local_addr()?,
UdpMode::Udpout | UdpMode::UdpBroadcast => socket.peer_addr()?,
};
Ok(Self {
address: address.to_string(),
mode,
read_timeout: None,
source: UdpSource::Socket(Arc::new(Mutex::new(Some(socket)))),
})
}
pub(crate) fn take_socket(&self) -> io::Result<Option<UdpSocket>> {
match &self.source {
UdpSource::Address => Ok(None),
UdpSource::Socket(socket) => socket
.lock()
.map_err(|_| io::Error::other("UDP socket lock poisoned"))?
.take()
.map(Some)
.ok_or_else(|| io::Error::other("UDP socket-backed configuration already used")),
}
}
pub fn read_timeout(mut self, timeout: Duration) -> Self {
self.read_timeout = Some(timeout);
self
}
}
impl Display for UdpConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mode = match self.mode {
UdpMode::Udpin => "udpin",
UdpMode::Udpout => "udpout",
UdpMode::UdpBroadcast => "udpbcast",
};
write!(f, "{mode}:{}", self.address)
}
}