use crate::error::Result as NodeResult;
use crate::storage::Storage;
use dashmap::DashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tracing::{debug, error, info};
#[derive(Clone)]
pub struct AgentStorageManager {
agent_storages: DashMap<String, Arc<Storage>>,
tap_root: Option<PathBuf>,
}
impl AgentStorageManager {
pub fn new(tap_root: Option<PathBuf>) -> Self {
info!("Creating AgentStorageManager with TAP root: {:?}", tap_root);
Self {
agent_storages: DashMap::new(),
tap_root,
}
}
pub async fn get_agent_storage(&self, agent_did: &str) -> NodeResult<Arc<Storage>> {
if let Some(storage) = self.agent_storages.get(agent_did) {
debug!("Using cached storage for agent: {}", agent_did);
return Ok(storage.clone());
}
debug!("Creating new storage for agent: {}", agent_did);
let storage = Storage::new_with_did(agent_did, self.tap_root.clone())
.await
.map_err(|e| {
crate::Error::Storage(format!(
"Failed to create storage for agent {}: {}",
agent_did, e
))
})?;
let storage_arc = Arc::new(storage);
self.agent_storages
.insert(agent_did.to_string(), storage_arc.clone());
info!("Created and cached storage for agent: {}", agent_did);
Ok(storage_arc)
}
pub fn get_cached_agent_storage(&self, agent_did: &str) -> Option<Arc<Storage>> {
self.agent_storages.get(agent_did).map(|s| s.clone())
}
pub fn remove_agent_storage(&self, agent_did: &str) -> Option<Arc<Storage>> {
debug!("Removing storage cache for agent: {}", agent_did);
self.agent_storages
.remove(agent_did)
.map(|(_, storage)| storage)
}
pub fn cached_storage_count(&self) -> usize {
self.agent_storages.len()
}
pub fn cached_agent_dids(&self) -> Vec<String> {
self.agent_storages
.iter()
.map(|entry| entry.key().clone())
.collect()
}
pub fn clear_cache(&self) {
info!("Clearing all cached agent storage instances");
self.agent_storages.clear();
}
pub async fn ensure_agent_storage(&self, agent_did: &str) -> NodeResult<()> {
match Storage::new_with_did(agent_did, self.tap_root.clone()).await {
Ok(_) => {
info!("Ensured storage exists for agent: {}", agent_did);
Ok(())
}
Err(e) => {
error!("Failed to ensure storage for agent {}: {}", agent_did, e);
Err(crate::Error::Storage(format!(
"Failed to ensure storage for agent {}: {}",
agent_did, e
)))
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[tokio::test]
async fn test_agent_storage_manager_creation() {
let temp_dir = TempDir::new().unwrap();
let manager = AgentStorageManager::new(Some(temp_dir.path().to_path_buf()));
assert_eq!(manager.cached_storage_count(), 0);
assert!(manager.cached_agent_dids().is_empty());
}
#[tokio::test]
async fn test_get_agent_storage() {
let temp_dir = TempDir::new().unwrap();
let manager = AgentStorageManager::new(Some(temp_dir.path().to_path_buf()));
let agent_did = "did:example:test-agent";
let storage1 = manager.get_agent_storage(agent_did).await.unwrap();
assert_eq!(manager.cached_storage_count(), 1);
let storage2 = manager.get_agent_storage(agent_did).await.unwrap();
assert_eq!(manager.cached_storage_count(), 1);
assert!(Arc::ptr_eq(&storage1, &storage2));
}
#[tokio::test]
async fn test_remove_agent_storage() {
let temp_dir = TempDir::new().unwrap();
let manager = AgentStorageManager::new(Some(temp_dir.path().to_path_buf()));
let agent_did = "did:example:test-agent";
let _storage = manager.get_agent_storage(agent_did).await.unwrap();
assert_eq!(manager.cached_storage_count(), 1);
let removed = manager.remove_agent_storage(agent_did);
assert!(removed.is_some());
assert_eq!(manager.cached_storage_count(), 0);
}
#[tokio::test]
async fn test_multiple_agents() {
let temp_dir = TempDir::new().unwrap();
let manager = AgentStorageManager::new(Some(temp_dir.path().to_path_buf()));
let agent1 = "did:example:agent1";
let agent2 = "did:example:agent2";
let _storage1 = manager.get_agent_storage(agent1).await.unwrap();
let _storage2 = manager.get_agent_storage(agent2).await.unwrap();
assert_eq!(manager.cached_storage_count(), 2);
let cached_dids = manager.cached_agent_dids();
assert!(cached_dids.contains(&agent1.to_string()));
assert!(cached_dids.contains(&agent2.to_string()));
}
}