use crate::underlying::SyncSerialPort;
use std::{
io::{Error as IoError, ErrorKind as IoErrorKind, IoSlice, IoSliceMut, Result as IoResult},
mem::{ManuallyDrop, forget},
os::windows::io::{AsRawHandle, FromRawHandle},
pin::Pin,
task::{Context, Poll},
time::Duration,
};
use tokio::{
io::{AsyncRead, AsyncWrite, ReadBuf},
net::windows::named_pipe::NamedPipeClient,
};
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
#[derive(Debug)]
pub struct RawAsyncSerialPort {
io: NamedPipeClient,
}
impl RawAsyncSerialPort {
pub fn new(mut inner: SyncSerialPort) -> IoResult<Self> {
inner.set_read_timeout(Duration::from_millis((u32::MAX - 1).into()))?;
inner.set_write_timeout(Duration::from_millis((u32::MAX - 1).into()))?;
let io = unsafe { NamedPipeClient::from_raw_handle(inner.as_raw_handle())? };
forget(inner);
Ok(Self { io })
}
pub fn try_clone(&self) -> IoResult<Self> {
Self::new(self.with_raw(SyncSerialPort::try_clone)?)
}
pub fn with_raw<F, R>(&self, function: F) -> R
where
F: FnOnce(&SyncSerialPort) -> R,
{
let serial_port =
ManuallyDrop::new(unsafe { SyncSerialPort::from_raw_handle(self.io.as_raw_handle()) });
function(&serial_port)
}
pub async fn read(&self, buf: &mut [u8]) -> IoResult<usize> {
loop {
self.io.readable().await?;
match self.io.try_read(buf) {
Ok(read) => return Ok(read),
Err(cause) => {
if cause.kind() == IoErrorKind::WouldBlock {
continue;
}
return Err(cause);
}
}
}
}
#[must_use]
pub const fn can_read_vectored() -> bool {
false
}
#[allow(
// Keep us equal with nix.
unused_variables,
// Need to match API with linux which does need an await.
clippy::unused_async,
)]
pub async fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
Err(IoError::from_raw_os_error(
windows::Win32::Networking::WinSock::WSAENOTSOCK.0,
))
}
pub async fn write(&self, buf: &[u8]) -> IoResult<usize> {
loop {
self.io.writable().await?;
match self.io.try_write(buf) {
Ok(n) => return Ok(n),
Err(cause) => {
if cause.kind() == IoErrorKind::WouldBlock {
continue;
}
return Err(cause);
}
}
}
}
#[must_use]
pub const fn can_write_vectored() -> bool {
false
}
#[allow(
// This is needed to keep us in sync with unix.
unused_variables,
// Need to match API with linux which does need an await.
clippy::unused_async,
)]
pub async fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> IoResult<usize> {
Err(IoError::from_raw_os_error(
windows::Win32::Networking::WinSock::WSAENOTSOCK.0,
))
}
pub fn poll_read(&mut self, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<IoResult<()>> {
AsyncRead::poll_read(Pin::new(&mut self.io), cx, buf)
}
pub fn poll_write(&mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> {
AsyncWrite::poll_write(Pin::new(&mut self.io), cx, buf)
}
#[allow(
// Kept us equal with nix in terms of api..
unused_variables,
// Not yet implemented
clippy::unused_self,
)]
pub fn poll_write_vectored(
&mut self,
ctx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<IoResult<usize>> {
Poll::Ready(Err(IoError::from_raw_os_error(
windows::Win32::Networking::WinSock::WSAENOTSOCK.0,
)))
}
#[allow(
// Cannot be implemented but needs to match unix.
clippy::unused_self,
)]
pub fn poll_shutdown(&mut self, _cx: &mut Context<'_>) -> Poll<IoResult<()>> {
Poll::Ready(Err(IoError::from_raw_os_error(
windows::Win32::Networking::WinSock::WSAENOTSOCK.0,
)))
}
}
const RAW_ASYNC_SERIAL_PORT_FIELDS: &[NamedField<'static>] = &[NamedField::new("io")];
impl Structable for RawAsyncSerialPort {
fn definition(&self) -> StructDef<'_> {
StructDef::new_static(
"RawAsyncSerialPortFields",
Fields::Named(RAW_ASYNC_SERIAL_PORT_FIELDS),
)
}
}
impl Valuable for RawAsyncSerialPort {
fn as_value(&self) -> Value<'_> {
Value::Structable(self)
}
fn visit(&self, visitor: &mut dyn Visit) {
visitor.visit_named_fields(&NamedValues::new(
RAW_ASYNC_SERIAL_PORT_FIELDS,
&[Valuable::as_value(&format!("{:?}", self.io))],
));
}
}