use std::io;
#[cfg(not(windows))]
use {io_lifetimes::AsFilelike, rustix::io::is_read_write};
#[cfg(windows)]
use {
std::{
os::windows::io::{AsRawSocket, RawSocket},
ptr,
},
windows_sys::Win32::Networking::WinSock::{
recv, send, MSG_PEEK, SOCKET, SOCKET_ERROR, WSAEFAULT, WSAESHUTDOWN, WSAEWOULDBLOCK,
},
};
pub trait IsReadWrite {
fn is_read_write(&self) -> io::Result<(bool, bool)>;
}
#[cfg(not(windows))]
impl<T: AsFilelike> IsReadWrite for T {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
Ok(is_read_write(self)?)
}
}
#[cfg(windows)]
impl IsReadWrite for std::fs::File {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
file_is_read_write(self)
}
}
#[cfg(all(windows, feature = "cap_std_impls"))]
impl IsReadWrite for cap_std::fs::File {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
use io_lifetimes::AsFilelike;
file_is_read_write(&self.as_filelike_view::<std::fs::File>())
}
}
#[cfg(all(windows, feature = "cap_std_impls_fs_utf8"))]
impl IsReadWrite for cap_std::fs_utf8::File {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
use io_lifetimes::AsFilelike;
file_is_read_write(&self.as_filelike_view::<std::fs::File>())
}
}
#[cfg(all(windows, feature = "async_std"))]
impl IsReadWrite for async_std::fs::File {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
use io_lifetimes::AsFilelike;
file_is_read_write(&self.as_filelike_view::<std::fs::File>())
}
}
#[cfg(all(windows, feature = "async_std"))]
impl IsReadWrite for cap_async_std::fs::File {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
use io_lifetimes::AsFilelike;
file_is_read_write(&self.as_filelike_view::<std::fs::File>())
}
}
#[cfg(all(windows, feature = "async_std", feature = "fs_utf8"))]
impl IsReadWrite for cap_async_std::fs_utf8::File {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
use io_lifetimes::AsFilelike;
file_is_read_write(&self.as_filelike_view::<std::fs::File>())
}
}
#[cfg(windows)]
impl IsReadWrite for std::net::TcpStream {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
raw_socket_is_read_write(self.as_raw_socket())
}
}
#[cfg(all(windows, feature = "cap_std_impls"))]
impl IsReadWrite for cap_std::net::TcpStream {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
raw_socket_is_read_write(self.as_raw_socket())
}
}
#[cfg(all(windows, feature = "cap_async_std_impls"))]
impl IsReadWrite for async_std::net::TcpStream {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
raw_socket_is_read_write(self.as_raw_socket())
}
}
#[cfg(all(windows, feature = "cap_async_std_impls"))]
impl IsReadWrite for cap_async_std::net::TcpStream {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
raw_socket_is_read_write(self.as_raw_socket())
}
}
#[cfg(windows)]
#[inline]
fn file_is_read_write(file: &std::fs::File) -> std::io::Result<(bool, bool)> {
cap_fs_ext::IsFileReadWrite::is_file_read_write(file)
}
#[cfg(windows)]
fn raw_socket_is_read_write(raw_socket: RawSocket) -> io::Result<(bool, bool)> {
let (mut read, mut write) = (true, true);
let socket = raw_socket as SOCKET;
let write_result = unsafe { send(socket, ptr::null_mut(), 0, 0) };
if write_result == SOCKET_ERROR {
let err = io::Error::last_os_error();
match err.raw_os_error() {
Some(WSAESHUTDOWN) => write = false,
Some(WSAEWOULDBLOCK) => (),
_ => return Err(err),
}
}
let read_result = unsafe { recv(socket, usize::MAX as *mut _, 1, MSG_PEEK) };
if read_result == SOCKET_ERROR {
let err = io::Error::last_os_error();
match err.raw_os_error() {
Some(WSAEFAULT) => (),
Some(WSAESHUTDOWN) => read = false,
_ => return Err(err),
}
}
Ok((read, write))
}
#[cfg(all(windows, feature = "socket2"))]
impl IsReadWrite for socket2::Socket {
#[inline]
fn is_read_write(&self) -> io::Result<(bool, bool)> {
use io_lifetimes::AsSocketlike;
self.as_socketlike_view::<std::net::TcpStream>()
.is_read_write()
}
}