use crate::error::Result;
use crate::ids::{SessionId, TenantId};
use bytes::Bytes;
use dashmap::DashMap;
use sha2::{Digest, Sha256};
use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct VconHandle {
pub url: String,
pub content_hash: String,
}
#[async_trait::async_trait]
pub trait VconStore: Send + Sync {
async fn put(
&self,
tenant_id: &TenantId,
session_id: &SessionId,
vcon_jws: Bytes,
) -> Result<VconHandle>;
async fn get(&self, handle: &VconHandle) -> Result<Option<Bytes>>;
async fn list_for_session(&self, session_id: &SessionId) -> Result<Vec<VconHandle>>;
}
#[derive(Clone, Debug, Default)]
pub struct MemoryVconStore {
inner: Arc<DashMap<String, Bytes>>,
seq: Arc<DashMap<String, u64>>,
}
impl MemoryVconStore {
pub fn new() -> Self {
Self::default()
}
}
#[async_trait::async_trait]
impl VconStore for MemoryVconStore {
async fn put(
&self,
_tenant_id: &TenantId,
session_id: &SessionId,
vcon_jws: Bytes,
) -> Result<VconHandle> {
let sid = session_id.to_string();
let n = {
let mut entry = self.seq.entry(sid.clone()).or_insert(0);
*entry += 1;
*entry
};
let url = format!("memory:vcon/{}/{}", sid, n);
let mut hasher = Sha256::new();
hasher.update(&vcon_jws);
let digest = hasher.finalize();
let content_hash = format!("sha256:{}", hex::encode(digest));
self.inner.insert(url.clone(), vcon_jws);
Ok(VconHandle { url, content_hash })
}
async fn get(&self, handle: &VconHandle) -> Result<Option<Bytes>> {
Ok(self.inner.get(&handle.url).map(|e| e.value().clone()))
}
async fn list_for_session(&self, session_id: &SessionId) -> Result<Vec<VconHandle>> {
let prefix = format!("memory:vcon/{}/", session_id);
let mut out = Vec::new();
for entry in self.inner.iter() {
if entry.key().starts_with(&prefix) {
let mut hasher = Sha256::new();
hasher.update(entry.value());
let digest = hasher.finalize();
out.push(VconHandle {
url: entry.key().clone(),
content_hash: format!("sha256:{}", hex::encode(digest)),
});
}
}
Ok(out)
}
}
mod hex {
pub fn encode(bytes: impl AsRef<[u8]>) -> String {
const HEX: &[u8; 16] = b"0123456789abcdef";
let bytes = bytes.as_ref();
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
s.push(HEX[(b >> 4) as usize] as char);
s.push(HEX[(b & 0x0f) as usize] as char);
}
s
}
}