use crate::underlying::SyncSerialPort;
use std::{
io::{Error as IoError, IoSlice, IoSliceMut, Result as IoResult},
os::fd::AsRawFd,
task::{Context, Poll, ready},
};
use tokio::io::{Interest, ReadBuf, unix::AsyncFd};
use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
#[derive(Debug)]
pub struct RawAsyncSerialPort {
io: AsyncFd<SyncSerialPort>,
}
impl RawAsyncSerialPort {
pub fn new(inner: SyncSerialPort) -> IoResult<Self> {
Ok(Self {
io: AsyncFd::new(inner)?,
})
}
pub fn try_clone(&self) -> IoResult<Self> {
Self::new(self.io.get_ref().try_clone()?)
}
pub fn with_raw<F, R>(&self, function: F) -> R
where
F: FnOnce(&SyncSerialPort) -> R,
{
function(self.io.get_ref())
}
pub async fn read(&self, buf: &mut [u8]) -> IoResult<usize> {
self.io
.async_io(Interest::READABLE, |inner| unsafe {
error_code_to_io_result(libc::read(
inner.as_raw_fd(),
buf.as_mut_ptr().cast(),
buf.len(),
))
})
.await
}
#[must_use]
pub const fn can_read_vectored() -> bool {
true
}
pub async fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> IoResult<usize> {
self.io
.async_io(Interest::READABLE, |inner| unsafe {
let buf_count = i32::try_from(bufs.len()).unwrap_or(i32::MAX);
error_code_to_io_result(libc::readv(
inner.as_raw_fd(),
bufs.as_mut_ptr().cast(),
buf_count,
))
})
.await
}
pub async fn write(&self, buf: &[u8]) -> IoResult<usize> {
self.io
.async_io(Interest::WRITABLE, |inner| unsafe {
error_code_to_io_result(libc::write(
inner.as_raw_fd(),
buf.as_ptr().cast(),
buf.len(),
))
})
.await
}
#[must_use]
pub const fn can_write_vectored() -> bool {
true
}
pub async fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> IoResult<usize> {
self.io
.async_io(Interest::WRITABLE, |inner| unsafe {
let buf_count = i32::try_from(bufs.len()).unwrap_or(i32::MAX);
error_code_to_io_result(libc::writev(
inner.as_raw_fd(),
bufs.as_ptr().cast(),
buf_count,
))
})
.await
}
pub fn poll_read(&mut self, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<IoResult<()>> {
loop {
let mut guard = ready!(self.io.poll_read_ready(cx)?);
let result = guard.try_io(|inner| unsafe {
let unfilled = buf.unfilled_mut();
error_code_to_io_result(libc::read(
inner.as_raw_fd(),
unfilled.as_mut_ptr().cast(),
unfilled.len(),
))
});
match result {
Ok(result) => {
let read = result?;
unsafe { buf.assume_init(read) };
buf.advance(read);
return Poll::Ready(Ok(()));
}
Err(_would_block) => {}
}
}
}
pub fn poll_write(&mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> {
loop {
let mut guard = ready!(self.io.poll_write_ready(cx)?);
let result = guard.try_io(|inner| {
error_code_to_io_result(unsafe {
libc::write(inner.as_raw_fd(), buf.as_ptr().cast(), buf.len())
})
});
match result {
Ok(result) => return Poll::Ready(result),
Err(_would_block) => {}
}
}
}
pub fn poll_write_vectored(
&mut self,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<IoResult<usize>> {
loop {
let mut guard = ready!(self.io.poll_write_ready(cx)?);
let result = guard.try_io(|inner| {
let buf_count = i32::try_from(bufs.len()).unwrap_or(i32::MAX);
error_code_to_io_result(unsafe {
libc::writev(inner.as_raw_fd(), bufs.as_ptr().cast(), buf_count)
})
});
match result {
Ok(result) => return Poll::Ready(result),
Err(_would_block) => {}
}
}
}
#[allow(
// Just unimplemented for now.
clippy::unused_self,
)]
pub fn poll_shutdown(&mut self, _cx: &mut Context<'_>) -> Poll<IoResult<()>> {
Poll::Ready(Err(IoError::from_raw_os_error(libc::ENOTSOCK)))
}
}
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(self.io.get_ref())],
));
}
}
#[allow(
// We manually check this sign to make sure it is not an issue.
clippy::cast_sign_loss,
)]
fn error_code_to_io_result(value: isize) -> IoResult<usize> {
if value < 0 {
Err(IoError::last_os_error())
} else {
Ok(value as usize)
}
}