use alloc::boxed::Box;
use core::time::Duration;
use ax_errno::{AxError, AxResult, LinuxError};
use enum_dispatch::enum_dispatch;
macro_rules! define_options {
($($name:ident($value:ty),)*) => {
#[allow(missing_docs)]
pub enum GetSocketOption<'a> {
$(
$name(&'a mut $value),
)*
}
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum SetSocketOption<'a> {
$(
$name(&'a $value),
)*
}
};
}
#[repr(C)]
#[derive(Default, Debug, Clone)]
pub struct UnixCredentials {
pub pid: u32,
pub uid: u32,
pub gid: u32,
}
impl UnixCredentials {
pub fn new(pid: u32) -> Self {
UnixCredentials {
pid,
uid: 0,
gid: 0,
}
}
}
define_options! {
ReuseAddress(bool),
Error(i32),
DontRoute(bool),
SendBuffer(usize),
ReceiveBuffer(usize),
KeepAlive(bool),
SendTimeout(Duration),
ReceiveTimeout(Duration),
SendBufferForce(usize),
PassCredentials(bool),
PeerCredentials(UnixCredentials),
NoDelay(bool),
MaxSegment(usize),
TcpKeepIdle(u32),
TcpKeepInterval(u32),
TcpKeepCount(u32),
TcpUserTimeout(u32),
TcpInfo(()),
Ttl(u8),
RecvErr(bool),
NonBlocking(bool),
}
#[enum_dispatch]
pub trait Configurable {
fn get_option_inner(&self, opt: &mut GetSocketOption) -> AxResult<bool>;
fn set_option_inner(&self, opt: SetSocketOption) -> AxResult<bool>;
fn get_option(&self, mut opt: GetSocketOption) -> AxResult {
self.get_option_inner(&mut opt).and_then(|supported| {
if !supported {
Err(AxError::from(LinuxError::ENOPROTOOPT))
} else {
Ok(())
}
})
}
fn set_option(&self, opt: SetSocketOption) -> AxResult {
self.set_option_inner(opt).and_then(|supported| {
if !supported {
Err(AxError::from(LinuxError::ENOPROTOOPT))
} else {
Ok(())
}
})
}
}
impl<T: Configurable + ?Sized> Configurable for Box<T> {
fn get_option_inner(&self, opt: &mut GetSocketOption) -> AxResult<bool> {
self.as_ref().get_option_inner(opt)
}
fn set_option_inner(&self, opt: SetSocketOption) -> AxResult<bool> {
self.as_ref().set_option_inner(opt)
}
}