use fjall::{Config, Database, Keyspace, KeyspaceCreateOptions};
#[derive(Clone)]
pub struct KvKeyspace {
ks: Keyspace,
}
impl KvKeyspace {
pub async fn get(&self, key: Vec<u8>) -> Result<Option<Vec<u8>>, String> {
let ks = self.ks.clone();
tokio::task::spawn_blocking(move || {
ks.get(&key)
.map(|opt| opt.map(|v| v.to_vec()))
.map_err(|e| e.to_string())
})
.await
.map_err(|e| format!("fjall blocking task failed: {e}"))?
}
pub async fn put(&self, key: Vec<u8>, value: Vec<u8>) -> Result<(), String> {
let ks = self.ks.clone();
tokio::task::spawn_blocking(move || ks.insert(&key, &value).map_err(|e| e.to_string()))
.await
.map_err(|e| format!("fjall blocking task failed: {e}"))?
}
pub async fn delete(&self, key: Vec<u8>) -> Result<(), String> {
let ks = self.ks.clone();
tokio::task::spawn_blocking(move || ks.remove(&key).map_err(|e| e.to_string()))
.await
.map_err(|e| format!("fjall blocking task failed: {e}"))?
}
pub async fn scan_prefix(&self, prefix: Vec<u8>) -> Result<Vec<(Vec<u8>, Vec<u8>)>, String> {
let ks = self.ks.clone();
tokio::task::spawn_blocking(move || {
let mut out = Vec::new();
for guard in ks.prefix(&prefix) {
let (k, v) = guard.into_inner().map_err(|e| e.to_string())?;
out.push((k.to_vec(), v.to_vec()));
}
Ok::<_, String>(out)
})
.await
.map_err(|e| format!("fjall blocking task failed: {e}"))?
}
}
pub struct MessagingStore {
_db: Database,
pub relationships: KvKeyspace,
pub outbox: KvKeyspace,
}
impl MessagingStore {
pub fn open(path: &str) -> Result<Self, String> {
let db = Database::open(Config::new(std::path::Path::new(path)))
.map_err(|e| format!("open messaging store at {path}: {e}"))?;
let relationships = KvKeyspace {
ks: db
.keyspace("relationships", KeyspaceCreateOptions::default)
.map_err(|e| format!("open relationships keyspace: {e}"))?,
};
let outbox = KvKeyspace {
ks: db
.keyspace("outbox", KeyspaceCreateOptions::default)
.map_err(|e| format!("open outbox keyspace: {e}"))?,
};
Ok(Self {
_db: db,
relationships,
outbox,
})
}
}