use std::future::Future;
use crate::CondSend;
pub struct ResourceLease<R, T>
where
R: CleanResource<T> + Send + Sync + 'static,
T: Send + 'static,
{
resource: Option<R>,
created: Vec<T>,
on_error: Option<Box<dyn Fn(crate::Error) + Send + Sync>>,
}
pub trait CleanResource<T> {
fn clean_resource(
&self,
resources: Vec<T>,
) -> impl Future<Output = Result<(), crate::Error>> + CondSend;
}
impl<R, T> ResourceLease<R, T>
where
R: CleanResource<T> + Send + Sync + 'static,
T: Send + 'static,
{
pub fn new(resource: R, on_error: impl Fn(crate::Error) + Send + Sync + 'static) -> Self {
Self {
resource: Some(resource),
created: Vec::new(),
on_error: Some(Box::new(on_error)),
}
}
pub fn new_println(resource: R) -> Self {
Self::new(resource, |e| {
println!("Error cleaning up leased resource: {}", e)
})
}
pub async fn for_create<F, Fut>(&mut self, create_fn: F) -> Result<(), crate::Error>
where
F: Fn() -> Fut + CondSend,
Fut: Future<Output = Result<Vec<T>, crate::Error>> + CondSend,
{
let created_resources = create_fn().await?;
self.created.extend(created_resources);
Ok(())
}
pub fn add_resources(&mut self, resource: impl IntoIterator<Item = T>) {
self.created.extend(resource);
}
pub fn resources(&self) -> &Vec<T> {
&self.created
}
pub async fn clean(mut self) -> Result<(), crate::Error> {
let resources = std::mem::take(&mut self.created);
let Some(resource) = self.resource.take() else {
return Ok(());
};
if resources.is_empty() {
return Ok(());
}
resource.clean_resource(resources).await
}
}
impl<R, T> Drop for ResourceLease<R, T>
where
R: CleanResource<T> + Send + Sync + 'static,
T: Send + 'static,
{
fn drop(&mut self) {
let resources = std::mem::take(&mut self.created);
let Some(resource) = self.resource.take() else {
return;
};
let on_error = self.on_error.take();
if resources.is_empty() {
return;
}
#[cfg(target_arch = "wasm32")]
{
let h = tokio_util::task::LocalPoolHandle::new(1);
h.spawn_pinned(|| async move {
let r = resource.clean_resource(resources).await;
if let Err(e) = r {
if let Some(on_error) = on_error {
on_error(e);
}
}
});
}
#[cfg(not(target_arch = "wasm32"))]
tokio::spawn(async move {
let r = resource.clean_resource(resources).await;
if let Err(e) = r {
if let Some(on_error) = on_error {
on_error(e);
}
}
});
}
}