use std::borrow::Cow;
use std::collections::BTreeMap;
use std::rc::Rc;
use super::Resource;
use super::ResourceHandle;
use super::ResourceHandleFd;
use super::ResourceHandleSocket;
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>, ResourceError> {
self
.index
.get(&rid)
.and_then(|rc| rc.downcast_rc::<T>())
.cloned()
.ok_or(ResourceError::BadResourceId)
}
pub fn get_any(
&self,
rid: ResourceId,
) -> Result<Rc<dyn Resource>, ResourceError> {
self
.index
.get(&rid)
.cloned()
.ok_or(ResourceError::BadResourceId)
}
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>, ResourceError> {
let resource = self.get::<T>(rid)?;
self.index.remove(&rid);
Ok(resource)
}
pub fn take_any(
&mut self,
rid: ResourceId,
) -> Result<Rc<dyn Resource>, ResourceError> {
self.index.remove(&rid).ok_or(ResourceError::BadResourceId)
}
#[deprecated = "This method may deadlock. Use take() and close() instead."]
pub fn close(&mut self, rid: ResourceId) -> Result<(), ResourceError> {
self
.index
.remove(&rid)
.ok_or(ResourceError::BadResourceId)
.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, ResourceError> {
let Some(handle) = self.get_any(rid)?.backing_handle() else {
return Err(ResourceError::BadResourceId);
};
let Some(fd) = handle.as_fd_like() else {
return Err(ResourceError::BadResourceId);
};
if !handle.is_valid() {
return Err(ResourceError::Reference);
}
Ok(fd)
}
pub fn get_socket(
&self,
rid: ResourceId,
) -> Result<ResourceHandleSocket, ResourceError> {
let Some(handle) = self.get_any(rid)?.backing_handle() else {
return Err(ResourceError::BadResourceId);
};
let Some(socket) = handle.as_socket_like() else {
return Err(ResourceError::BadResourceId);
};
if !handle.is_valid() {
return Err(ResourceError::Reference);
}
Ok(socket)
}
pub fn get_handle(
&self,
rid: ResourceId,
) -> Result<ResourceHandle, ResourceError> {
let Some(handle) = self.get_any(rid)?.backing_handle() else {
return Err(ResourceError::BadResourceId);
};
if !handle.is_valid() {
return Err(ResourceError::Reference);
}
Ok(handle)
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum ResourceError {
#[class(reference)]
#[error("null or invalid handle")]
Reference,
#[class("BadResource")]
#[error("Bad resource ID")]
BadResourceId,
#[class("Busy")]
#[error("Resource is unavailable because it is in use by a promise")]
Unavailable,
#[class("BadResource")]
#[error("{0}")]
Other(String),
}