use std::any::Any;
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use serde::Serialize;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
type CleanupTask = Box<dyn FnOnce() -> BoxFuture<'static, ()> + Send>;
#[derive(Default, Clone, Serialize)]
pub struct ResolvedDependencies {
#[serde(skip)]
dependencies: Arc<Mutex<HashMap<String, Arc<dyn Any + Send + Sync>>>>,
#[serde(skip)]
cleanup_tasks: Arc<Mutex<Vec<CleanupTask>>>,
}
impl ResolvedDependencies {
#[must_use]
pub fn new() -> Self {
Self {
dependencies: Arc::new(Mutex::new(HashMap::new())),
cleanup_tasks: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn insert(&mut self, key: String, value: Arc<dyn Any + Send + Sync>) {
self.dependencies.lock().unwrap().insert(key, value);
}
#[must_use]
pub fn get<T: Send + Sync + 'static>(&self, key: &str) -> Option<Arc<T>> {
self.dependencies
.lock()
.unwrap()
.get(key)
.and_then(|value| Arc::clone(value).downcast::<T>().ok())
}
#[must_use]
pub fn get_arc(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
self.dependencies.lock().unwrap().get(key).cloned()
}
#[must_use]
pub fn contains(&self, key: &str) -> bool {
self.dependencies.lock().unwrap().contains_key(key)
}
#[must_use]
pub fn keys(&self) -> Vec<String> {
self.dependencies.lock().unwrap().keys().cloned().collect()
}
pub fn add_cleanup_task(&self, task: CleanupTask) {
self.cleanup_tasks.lock().unwrap().push(task);
}
pub async fn cleanup(self) {
let tasks = {
let mut cleanup_tasks = self.cleanup_tasks.lock().unwrap();
std::mem::take(&mut *cleanup_tasks)
};
for task in tasks.into_iter().rev() {
task().await;
}
}
}
impl std::fmt::Debug for ResolvedDependencies {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let deps = self.dependencies.lock().unwrap();
let tasks = self.cleanup_tasks.lock().unwrap();
f.debug_struct("ResolvedDependencies")
.field("dependencies", &deps.keys())
.field("cleanup_tasks_count", &tasks.len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new() {
let resolved = ResolvedDependencies::new();
assert!(!resolved.contains("anything"));
}
#[test]
fn test_insert_and_get() {
let mut resolved = ResolvedDependencies::new();
let value = Arc::new(42i32);
resolved.insert("answer".to_string(), value);
let retrieved: Option<Arc<i32>> = resolved.get("answer");
assert_eq!(retrieved.map(|v| *v), Some(42));
}
#[test]
fn test_get_type_mismatch() {
let mut resolved = ResolvedDependencies::new();
resolved.insert("number".to_string(), Arc::new(42i32));
let wrong: Option<Arc<String>> = resolved.get("number");
assert!(wrong.is_none());
}
#[test]
fn test_get_missing() {
let resolved = ResolvedDependencies::new();
let missing: Option<Arc<i32>> = resolved.get("missing");
assert!(missing.is_none());
}
#[test]
fn test_get_arc() {
let mut resolved = ResolvedDependencies::new();
resolved.insert("data".to_string(), Arc::new(vec![1, 2, 3]));
let any_ref = resolved.get_arc("data");
assert!(any_ref.is_some());
let vec_ref = any_ref.unwrap().downcast::<Vec<i32>>().ok();
assert!(vec_ref.is_some());
}
#[test]
fn test_contains() {
let mut resolved = ResolvedDependencies::new();
resolved.insert("exists".to_string(), Arc::new(true));
assert!(resolved.contains("exists"));
assert!(!resolved.contains("missing"));
}
#[tokio::test]
async fn test_cleanup_order() {
let order = Arc::new(Mutex::new(Vec::new()));
let resolved = ResolvedDependencies::new();
let order1 = order.clone();
resolved.add_cleanup_task(Box::new(move || {
Box::pin(async move {
order1.lock().unwrap().push(1);
})
}));
let order2 = order.clone();
resolved.add_cleanup_task(Box::new(move || {
Box::pin(async move {
order2.lock().unwrap().push(2);
})
}));
let order3 = order.clone();
resolved.add_cleanup_task(Box::new(move || {
Box::pin(async move {
order3.lock().unwrap().push(3);
})
}));
resolved.cleanup().await;
assert_eq!(*order.lock().unwrap(), vec![3, 2, 1]);
}
#[tokio::test]
async fn test_cleanup_empty() {
let resolved = ResolvedDependencies::new();
resolved.cleanup().await;
}
#[test]
fn test_clone() {
let mut resolved1 = ResolvedDependencies::new();
resolved1.insert("key".to_string(), Arc::new(42i32));
let resolved2 = resolved1.clone();
let value: Option<Arc<i32>> = resolved2.get("key");
assert_eq!(value.map(|v| *v), Some(42));
}
}