use crate::error::Result;
use agentdb::{AgentDB, Value};
use async_trait::async_trait;
use std::sync::Arc;
#[async_trait]
pub trait KvStore: Send + Sync {
async fn set(&self, key: &str, value: &[u8]) -> Result<()>;
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>>;
async fn delete(&self, key: &str) -> Result<()>;
async fn exists(&self, key: &str) -> Result<bool>;
async fn scan(&self, prefix: &str) -> Result<Vec<String>>;
}
pub struct DbKvStore {
db: Arc<Box<dyn AgentDB>>,
namespace: String,
}
impl DbKvStore {
pub fn new(db: Arc<Box<dyn AgentDB>>, namespace: String) -> Self {
Self { db, namespace }
}
fn namespaced_key(&self, key: &str) -> String {
format!("kv:{}:{}", self.namespace, key)
}
fn strip_namespace(&self, key: &str) -> String {
key.strip_prefix(&format!("kv:{}:", self.namespace))
.unwrap_or(key)
.to_string()
}
}
#[async_trait]
impl KvStore for DbKvStore {
async fn set(&self, key: &str, value: &[u8]) -> Result<()> {
let namespaced = self.namespaced_key(key);
self.db.put(&namespaced, Value::from(value)).await?;
Ok(())
}
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
let namespaced = self.namespaced_key(key);
match self.db.get(&namespaced).await? {
Some(value) => Ok(Some(value.as_bytes().to_vec())),
None => Ok(None),
}
}
async fn delete(&self, key: &str) -> Result<()> {
let namespaced = self.namespaced_key(key);
self.db.delete(&namespaced).await?;
Ok(())
}
async fn exists(&self, key: &str) -> Result<bool> {
let namespaced = self.namespaced_key(key);
Ok(self.db.exists(&namespaced).await?)
}
async fn scan(&self, prefix: &str) -> Result<Vec<String>> {
let namespaced_prefix = self.namespaced_key(prefix);
let result = self.db.scan(&namespaced_prefix).await?;
let keys = result
.keys
.into_iter()
.map(|k| self.strip_namespace(&k))
.collect();
Ok(keys)
}
}