use cfg_if::cfg_if;
use crate::sys;
use socket2::Socket;
use std::io;
cfg_if! {
if #[cfg(feature = "tokio")] {
mod tokio;
pub use self::tokio::*;
}
}
#[derive(Debug, derive_more::From)]
#[non_exhaustive]
pub enum AnyStdSocket {
TcpListener(std::net::TcpListener),
TcpStream(std::net::TcpStream),
UdpSocket(std::net::UdpSocket),
#[cfg(unix)] UnixDatagram(std::os::unix::net::UnixDatagram),
#[cfg(unix)] UnixListener(std::os::unix::net::UnixListener),
#[cfg(unix)] UnixStream(std::os::unix::net::UnixStream),
#[from(ignore)]
Other(Socket),
}
impl TryFrom<Socket> for AnyStdSocket {
type Error = io::Error;
#[allow(clippy::needless_late_init)] fn try_from(socket: Socket) -> Result<Self, Self::Error> {
let address: socket2::SockAddr = socket.local_addr()?;
let domain: socket2::Domain = address.domain();
let state: SocketState = sys::get_socket_state(&socket)?;
let is_connected: bool = {
if
state.r#type != socket2::Type::STREAM ||
state.is_listening == Some(true)
{
false
}
else { match socket.peer_addr() {
Ok(_) => true,
Err(error) if error.kind() == io::ErrorKind::NotConnected => false,
Err(error) => return Err(error),
}}
};
Ok(match (domain, state.r#type, state.protocol, state.is_listening, is_connected) {
(
socket2::Domain::IPV4 | socket2::Domain::IPV6,
socket2::Type::STREAM,
None,
None | Some(true),
false,
) | (
_,
_,
Some(socket2::Protocol::TCP),
None | Some(true),
false,
) => Self::TcpListener(socket.into()),
(
socket2::Domain::IPV4 | socket2::Domain::IPV6,
socket2::Type::STREAM,
None,
Some(false),
true,
) | (
_,
_,
Some(socket2::Protocol::TCP),
Some(false),
true,
) => Self::TcpStream(socket.into()),
(
socket2::Domain::IPV4 | socket2::Domain::IPV6,
socket2::Type::DGRAM,
None,
_,
_,
) | (
_,
_,
Some(socket2::Protocol::UDP),
_,
_,
) => Self::UdpSocket(socket.into()),
#[cfg(unix)]
(
socket2::Domain::UNIX,
socket2::Type::STREAM,
_,
None | Some(true),
false,
) => Self::UnixListener(socket.into()),
#[cfg(unix)]
(
socket2::Domain::UNIX,
socket2::Type::STREAM,
_,
Some(false),
true,
) => Self::UnixStream(socket.into()),
#[cfg(unix)]
(
socket2::Domain::UNIX,
socket2::Type::DGRAM,
_,
_,
_,
) => Self::UnixDatagram(socket.into()),
_ => Self::Other(socket),
})
}
}
impl From<AnyStdSocket> for Socket {
fn from(socket: AnyStdSocket) -> Self {
match socket {
AnyStdSocket::TcpListener(s) => s.into(),
AnyStdSocket::TcpStream(s) => s.into(),
AnyStdSocket::UdpSocket(s) => s.into(),
#[cfg(unix)] AnyStdSocket::UnixDatagram(s) => s.into(),
#[cfg(unix)] AnyStdSocket::UnixListener(s) => s.into(),
#[cfg(unix)] AnyStdSocket::UnixStream(s) => s.into(),
AnyStdSocket::Other(s) => s,
}
}
}
pub(crate) struct SocketState {
pub r#type: socket2::Type,
pub protocol: Option<socket2::Protocol>,
pub is_listening: Option<bool>,
}