use std::ops::Deref;
use std::sync::Mutex;
pub trait Resource {
type Output;
type Error;
fn try_create(&self) -> Result<Self::Output, Self::Error>;
}
pub struct ResourcePool<R: Resource> {
factory: R,
resources: Mutex<Vec<R::Output>>,
}
impl<R: Resource> std::fmt::Debug for ResourcePool<R> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ResourcePool")
.field("factory", &"<not shown>")
.field(
"resources.len()",
&match self.resources.try_lock() {
Ok(resources) => resources.len().to_string(),
Err(_) => "<could not determine>".to_string(),
},
)
.finish()
}
}
pub struct ResourceHandle<'pool, R: Resource> {
parent: &'pool ResourcePool<R>,
inner: Option<R::Output>,
}
impl<'a, R: Resource> Drop for ResourceHandle<'a, R> {
fn drop(&mut self) {
let mut resources = self
.parent
.resources
.lock()
.expect("Poisoned mutex for ResourceHandle");
resources.push(self.inner.take().unwrap());
}
}
impl<'a, R: Resource> Deref for ResourceHandle<'a, R> {
type Target = R::Output;
fn deref(&self) -> &Self::Target {
self.inner.as_ref().unwrap()
}
}
impl<R: Resource> ResourcePool<R> {
pub fn new(factory: R) -> Self {
ResourcePool {
factory,
resources: Default::default(),
}
}
pub fn try_create(&self) -> Result<ResourceHandle<R>, R::Error> {
let resource = {
let mut resources = self
.resources
.lock()
.expect("Poisoned mutex for ResourcePool");
let resource = resources.pop();
match resource {
Some(resource) => resource,
None => self.factory.try_create()?,
}
};
Ok(ResourceHandle {
parent: self,
inner: Some(resource),
})
}
}