use parking_lot::Mutex;
use std::any::Any;
pub struct RunResources {
entries: Mutex<Vec<Box<dyn Any + Send>>>,
}
impl RunResources {
pub(crate) fn new() -> Self {
Self {
entries: Mutex::new(Vec::new()),
}
}
pub fn put<R: Send + 'static>(&self, resource: R) -> &Self {
self.entries.lock().push(Box::new(resource));
self
}
pub fn with<R, F, T>(&self, f: F) -> Option<T>
where
R: Send + 'static,
F: FnOnce(&R) -> T,
{
let entries = self.entries.lock();
entries.iter().rev().find_map(|entry| entry.downcast_ref::<R>()).map(f)
}
pub fn take<R: Send + 'static>(&self) -> Option<R> {
let mut entries = self.entries.lock();
let position = entries.iter().rposition(|entry| entry.is::<R>())?;
let taken = entries.remove(position);
taken.downcast::<R>().ok().map(|boxed| *boxed)
}
pub fn take_guard<R: Send + 'static>(&self) -> Option<TakenResource<'_, R>> {
self.take::<R>().map(|value| TakenResource {
value: Some(value),
bag: self,
})
}
pub fn len(&self) -> usize {
self.entries.lock().len()
}
pub fn is_empty(&self) -> bool {
self.entries.lock().is_empty()
}
pub(crate) fn release_all(&self) -> usize {
let mut taken = std::mem::take(&mut *self.entries.lock());
let count = taken.len();
while let Some(resource) = taken.pop() {
drop(resource);
}
count
}
}
impl std::fmt::Debug for RunResources {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RunResources").field("held", &self.len()).finish()
}
}
pub struct TakenResource<'a, R: Send + 'static> {
value: Option<R>,
bag: &'a RunResources,
}
impl<R: Send + 'static> TakenResource<'_, R> {
pub fn keep(mut self) -> R {
self.value.take().expect("a live guard always holds its value")
}
}
impl<R: Send + 'static> std::ops::Deref for TakenResource<'_, R> {
type Target = R;
fn deref(&self) -> &R {
self.value.as_ref().expect("a live guard always holds its value")
}
}
impl<R: Send + 'static> std::ops::DerefMut for TakenResource<'_, R> {
fn deref_mut(&mut self) -> &mut R {
self.value.as_mut().expect("a live guard always holds its value")
}
}
impl<R: Send + 'static> Drop for TakenResource<'_, R> {
fn drop(&mut self) {
if let Some(value) = self.value.take() {
self.bag.put(value);
}
}
}
impl<R: Send + 'static + std::fmt::Debug> std::fmt::Debug for TakenResource<'_, R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TakenResource").field(&self.value).finish()
}
}