use crate::error::bad_resource_id;
use crate::error::custom_error;
use crate::error::not_supported;
use crate::io::BufMutView;
use crate::io::BufView;
use crate::io::WriteOutcome;
use anyhow::Error;
use futures::Future;
use std::any::type_name;
use std::any::Any;
use std::any::TypeId;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::io::IsTerminal;
use std::iter::Iterator;
use std::pin::Pin;
use std::rc::Rc;
pub type AsyncResult<T> = Pin<Box<dyn Future<Output = Result<T, Error>>>>;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[allow(unused)]
pub enum ResourceHandle {
Fd(ResourceHandleFd),
Socket(ResourceHandleSocket),
}
#[cfg(unix)]
pub type ResourceHandleFd = std::os::fd::RawFd;
#[cfg(unix)]
pub type ResourceHandleSocket = std::os::fd::RawFd;
#[cfg(windows)]
pub type ResourceHandleFd = std::os::windows::io::RawHandle;
#[cfg(windows)]
pub type ResourceHandleSocket = std::os::windows::io::RawSocket;
impl ResourceHandle {
#[cfg(windows)]
pub fn from_fd_like(io: &impl std::os::windows::io::AsRawHandle) -> Self {
Self::Fd(io.as_raw_handle())
}
#[cfg(unix)]
pub fn from_fd_like(io: &impl std::os::unix::io::AsRawFd) -> Self {
Self::Fd(io.as_raw_fd())
}
#[cfg(windows)]
pub fn from_socket_like(io: &impl std::os::windows::io::AsRawSocket) -> Self {
Self::Socket(io.as_raw_socket())
}
#[cfg(unix)]
pub fn from_socket_like(io: &impl std::os::unix::io::AsRawFd) -> Self {
Self::Socket(io.as_raw_fd())
}
pub fn is_valid(&self) -> bool {
#[cfg(windows)]
{
match self {
Self::Fd(handle) => {
!handle.is_null()
&& *handle != -1_isize as std::os::windows::io::RawHandle
}
Self::Socket(socket) => {
*socket != -1_i64 as std::os::windows::io::RawSocket
}
}
}
#[cfg(unix)]
{
match self {
Self::Fd(fd) => *fd >= 0,
Self::Socket(fd) => *fd >= 0,
}
}
}
pub fn as_fd_like(&self) -> Option<ResourceHandleFd> {
match self {
Self::Fd(fd) => Some(*fd),
_ => None,
}
}
pub fn as_socket_like(&self) -> Option<ResourceHandleSocket> {
match self {
Self::Socket(socket) => Some(*socket),
_ => None,
}
}
pub fn is_terminal(&self) -> bool {
match self {
Self::Fd(fd) if self.is_valid() => {
#[cfg(windows)]
{
unsafe {
std::os::windows::io::BorrowedHandle::borrow_raw(*fd).is_terminal()
}
}
#[cfg(unix)]
{
unsafe { std::os::fd::BorrowedFd::borrow_raw(*fd).is_terminal() }
}
}
_ => false,
}
}
}
pub trait Resource: Any + 'static {
fn name(&self) -> Cow<str> {
type_name::<Self>().into()
}
fn read(self: Rc<Self>, limit: usize) -> AsyncResult<BufView> {
_ = limit;
Box::pin(futures::future::err(not_supported()))
}
fn read_byob(
self: Rc<Self>,
mut buf: BufMutView,
) -> AsyncResult<(usize, BufMutView)> {
Box::pin(async move {
let read = self.read(buf.len()).await?;
let nread = read.len();
buf[..nread].copy_from_slice(&read);
Ok((nread, buf))
})
}
fn write_error(self: Rc<Self>, _error: Error) -> AsyncResult<()> {
Box::pin(futures::future::err(not_supported()))
}
fn write(self: Rc<Self>, buf: BufView) -> AsyncResult<WriteOutcome> {
_ = buf;
Box::pin(futures::future::err(not_supported()))
}
fn write_all(self: Rc<Self>, view: BufView) -> AsyncResult<()> {
Box::pin(async move {
let mut view = view;
let this = self;
while !view.is_empty() {
let resp = this.clone().write(view).await?;
match resp {
WriteOutcome::Partial {
nwritten,
view: new_view,
} => {
view = new_view;
view.advance_cursor(nwritten);
}
WriteOutcome::Full { .. } => break,
}
}
Ok(())
})
}
fn read_byob_sync(self: Rc<Self>, data: &mut [u8]) -> Result<usize, Error> {
_ = data;
Err(not_supported())
}
fn write_sync(self: Rc<Self>, data: &[u8]) -> Result<usize, Error> {
_ = data;
Err(not_supported())
}
fn shutdown(self: Rc<Self>) -> AsyncResult<()> {
Box::pin(futures::future::err(not_supported()))
}
fn close(self: Rc<Self>) {}
fn backing_handle(self: Rc<Self>) -> Option<ResourceHandle> {
#[allow(deprecated)]
self.backing_fd().map(ResourceHandle::Fd)
}
#[deprecated = "Use backing_handle"]
fn backing_fd(self: Rc<Self>) -> Option<ResourceHandleFd> {
None
}
fn size_hint(&self) -> (u64, Option<u64>) {
(0, None)
}
}
impl dyn Resource {
#[inline(always)]
fn is<T: Resource>(&self) -> bool {
self.type_id() == TypeId::of::<T>()
}
#[inline(always)]
#[allow(clippy::needless_lifetimes)]
pub fn downcast_rc<'a, T: Resource>(self: &'a Rc<Self>) -> Option<&'a Rc<T>> {
if self.is::<T>() {
let ptr = self as *const Rc<_> as *const Rc<T>;
#[allow(clippy::undocumented_unsafe_blocks)]
Some(unsafe { &*ptr })
} else {
None
}
}
}
pub type ResourceId = u32;
#[derive(Default)]
pub struct ResourceTable {
index: BTreeMap<ResourceId, Rc<dyn Resource>>,
next_rid: ResourceId,
}
impl ResourceTable {
pub fn len(&self) -> usize {
self.index.len()
}
pub fn is_empty(&self) -> bool {
self.index.is_empty()
}
pub fn add<T: Resource>(&mut self, resource: T) -> ResourceId {
self.add_rc(Rc::new(resource))
}
pub fn add_rc<T: Resource>(&mut self, resource: Rc<T>) -> ResourceId {
let resource = resource as Rc<dyn Resource>;
self.add_rc_dyn(resource)
}
pub fn add_rc_dyn(&mut self, resource: Rc<dyn Resource>) -> ResourceId {
let rid = self.next_rid;
let removed_resource = self.index.insert(rid, resource);
assert!(removed_resource.is_none());
self.next_rid += 1;
rid
}
pub fn has(&self, rid: ResourceId) -> bool {
self.index.contains_key(&rid)
}
pub fn get<T: Resource>(&self, rid: ResourceId) -> Result<Rc<T>, Error> {
self
.index
.get(&rid)
.and_then(|rc| rc.downcast_rc::<T>())
.map(Clone::clone)
.ok_or_else(bad_resource_id)
}
pub fn get_any(&self, rid: ResourceId) -> Result<Rc<dyn Resource>, Error> {
self
.index
.get(&rid)
.map(Clone::clone)
.ok_or_else(bad_resource_id)
}
pub fn replace<T: Resource>(&mut self, rid: ResourceId, resource: T) {
let result = self
.index
.insert(rid, Rc::new(resource) as Rc<dyn Resource>);
assert!(result.is_some());
}
pub fn take<T: Resource>(&mut self, rid: ResourceId) -> Result<Rc<T>, Error> {
let resource = self.get::<T>(rid)?;
self.index.remove(&rid);
Ok(resource)
}
pub fn take_any(
&mut self,
rid: ResourceId,
) -> Result<Rc<dyn Resource>, Error> {
self.index.remove(&rid).ok_or_else(bad_resource_id)
}
#[deprecated = "This method may deadlock. Use take() and close() instead."]
pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> {
self
.index
.remove(&rid)
.ok_or_else(bad_resource_id)
.map(|resource| resource.close())
}
pub fn names(&self) -> impl Iterator<Item = (ResourceId, Cow<str>)> {
self
.index
.iter()
.map(|(&id, resource)| (id, resource.name()))
}
pub fn get_fd(&self, rid: ResourceId) -> Result<ResourceHandleFd, Error> {
let Some(handle) = self.get_any(rid)?.backing_handle() else {
return Err(bad_resource_id());
};
let Some(fd) = handle.as_fd_like() else {
return Err(bad_resource_id());
};
if !handle.is_valid() {
return Err(custom_error("ReferenceError", "null or invalid handle"));
}
Ok(fd)
}
pub fn get_socket(
&self,
rid: ResourceId,
) -> Result<ResourceHandleSocket, Error> {
let Some(handle) = self.get_any(rid)?.backing_handle() else {
return Err(bad_resource_id());
};
let Some(socket) = handle.as_socket_like() else {
return Err(bad_resource_id());
};
if !handle.is_valid() {
return Err(custom_error("ReferenceError", "null or invalid handle"));
}
Ok(socket)
}
pub fn get_handle(
&self,
rid: ResourceId,
) -> ::std::result::Result<ResourceHandle, ::anyhow::Error> {
let Some(handle) = self.get_any(rid)?.backing_handle() else {
return Err(bad_resource_id());
};
if !handle.is_valid() {
return Err(custom_error("ReferenceError", "null or invalid handle"));
}
Ok(handle)
}
}
#[macro_export]
macro_rules! impl_readable_byob {
() => {
fn read(
self: ::std::rc::Rc<Self>,
limit: ::core::primitive::usize,
) -> AsyncResult<$crate::BufView> {
::std::boxed::Box::pin(async move {
let mut vec = ::std::vec![0; limit];
let nread = self.read(&mut vec).await?;
if nread != vec.len() {
vec.truncate(nread);
}
let view = $crate::BufView::from(vec);
::std::result::Result::Ok(view)
})
}
fn read_byob(
self: ::std::rc::Rc<Self>,
mut buf: $crate::BufMutView,
) -> AsyncResult<(::core::primitive::usize, $crate::BufMutView)> {
::std::boxed::Box::pin(async move {
let nread = self.read(buf.as_mut()).await?;
::std::result::Result::Ok((nread, buf))
})
}
};
}
#[macro_export]
macro_rules! impl_writable {
(__write) => {
fn write(
self: ::std::rc::Rc<Self>,
view: $crate::BufView,
) -> $crate::AsyncResult<$crate::WriteOutcome> {
::std::boxed::Box::pin(async move {
let nwritten = self.write(&view).await?;
::std::result::Result::Ok($crate::WriteOutcome::Partial {
nwritten,
view,
})
})
}
};
(__write_all) => {
fn write_all(
self: ::std::rc::Rc<Self>,
view: $crate::BufView,
) -> $crate::AsyncResult<()> {
::std::boxed::Box::pin(async move {
self.write_all(&view).await?;
::std::result::Result::Ok(())
})
}
};
() => {
$crate::impl_writable!(__write);
};
(with_all) => {
$crate::impl_writable!(__write);
$crate::impl_writable!(__write_all);
};
}