use async_trait::async_trait;
use bytes::Bytes;
use crate::{LLError, LLPath};
#[async_trait]
pub trait AsyncLLReader: Send + Sync {
async fn ll_read_async(&mut self, path: &[&[u8]]) -> Result<Option<Bytes>, LLError>;
}
#[async_trait]
pub trait AsyncLLWriter: Send + Sync {
async fn ll_write_async(&mut self, path: &[&[u8]], data: Bytes) -> Result<LLPath, LLError>;
}
pub trait AsyncLLStore: AsyncLLReader + AsyncLLWriter {}
impl<T: AsyncLLReader + AsyncLLWriter> AsyncLLStore for T {}
#[async_trait]
impl<T: AsyncLLReader + ?Sized> AsyncLLReader for &mut T {
async fn ll_read_async(&mut self, path: &[&[u8]]) -> Result<Option<Bytes>, LLError> {
(*self).ll_read_async(path).await
}
}
#[async_trait]
impl<T: AsyncLLWriter + ?Sized> AsyncLLWriter for &mut T {
async fn ll_write_async(&mut self, path: &[&[u8]], data: Bytes) -> Result<LLPath, LLError> {
(*self).ll_write_async(path, data).await
}
}
#[async_trait]
impl<T: AsyncLLReader + ?Sized> AsyncLLReader for Box<T> {
async fn ll_read_async(&mut self, path: &[&[u8]]) -> Result<Option<Bytes>, LLError> {
self.as_mut().ll_read_async(path).await
}
}
#[async_trait]
impl<T: AsyncLLWriter + ?Sized> AsyncLLWriter for Box<T> {
async fn ll_write_async(&mut self, path: &[&[u8]], data: Bytes) -> Result<LLPath, LLError> {
self.as_mut().ll_write_async(path, data).await
}
}
pub struct SyncToAsyncLL<T> {
inner: std::sync::Arc<std::sync::Mutex<T>>,
}
impl<T> SyncToAsyncLL<T> {
pub fn new(inner: T) -> Self {
Self {
inner: std::sync::Arc::new(std::sync::Mutex::new(inner)),
}
}
pub fn inner(&self) -> &std::sync::Mutex<T> {
&self.inner
}
}
impl<T> Clone for SyncToAsyncLL<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
#[async_trait]
impl<T: crate::LLReader + Send + 'static> AsyncLLReader for SyncToAsyncLL<T> {
async fn ll_read_async(&mut self, path: &[&[u8]]) -> Result<Option<Bytes>, LLError> {
let path_owned: Vec<Vec<u8>> = path.iter().map(|c| c.to_vec()).collect();
let inner = self.inner.clone();
let mut guard = inner.lock().map_err(|_| LLError::Protocol {
code: 100,
detail: Bytes::from_static(b"lock poisoned"),
})?;
let refs: Vec<&[u8]> = path_owned.iter().map(|v| v.as_slice()).collect();
guard.ll_read(&refs)
}
}
#[async_trait]
impl<T: crate::LLWriter + Send + 'static> AsyncLLWriter for SyncToAsyncLL<T> {
async fn ll_write_async(&mut self, path: &[&[u8]], data: Bytes) -> Result<LLPath, LLError> {
let path_owned: Vec<Vec<u8>> = path.iter().map(|c| c.to_vec()).collect();
let inner = self.inner.clone();
let mut guard = inner.lock().map_err(|_| LLError::Protocol {
code: 100,
detail: Bytes::from_static(b"lock poisoned"),
})?;
let refs: Vec<&[u8]> = path_owned.iter().map(|v| v.as_slice()).collect();
guard.ll_write(&refs, data)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
struct TestAsyncLLStore {
data: HashMap<Vec<Vec<u8>>, Bytes>,
}
impl TestAsyncLLStore {
fn new() -> Self {
Self {
data: HashMap::new(),
}
}
}
#[async_trait]
impl AsyncLLReader for TestAsyncLLStore {
async fn ll_read_async(&mut self, path: &[&[u8]]) -> Result<Option<Bytes>, LLError> {
let key: Vec<Vec<u8>> = path.iter().map(|c| c.to_vec()).collect();
Ok(self.data.get(&key).cloned())
}
}
#[async_trait]
impl AsyncLLWriter for TestAsyncLLStore {
async fn ll_write_async(&mut self, path: &[&[u8]], data: Bytes) -> Result<LLPath, LLError> {
let key: Vec<Vec<u8>> = path.iter().map(|c| c.to_vec()).collect();
self.data.insert(key, data);
Ok(path.iter().map(|c| Bytes::copy_from_slice(c)).collect())
}
}
#[tokio::test]
async fn async_read_write_works() {
let mut store = TestAsyncLLStore::new();
let path = &[b"users".as_slice(), b"123".as_slice()];
let data = Bytes::from_static(b"hello async");
store.ll_write_async(path, data.clone()).await.unwrap();
let result = store.ll_read_async(path).await.unwrap();
assert_eq!(result, Some(data));
let result = store.ll_read_async(&[b"nonexistent"]).await.unwrap();
assert_eq!(result, None);
}
#[tokio::test]
async fn object_safety_works() {
let mut store = TestAsyncLLStore::new();
let boxed: &mut dyn AsyncLLStore = &mut store;
boxed
.ll_write_async(&[b"test"], Bytes::from_static(b"data"))
.await
.unwrap();
let result = boxed.ll_read_async(&[b"test"]).await.unwrap();
assert_eq!(result, Some(Bytes::from_static(b"data")));
}
#[tokio::test]
async fn sync_to_async_adapter_works() {
use crate::traits::{LLReader, LLWriter};
struct SyncStore {
data: HashMap<Vec<Vec<u8>>, Bytes>,
}
impl LLReader for SyncStore {
fn ll_read(&mut self, path: &[&[u8]]) -> Result<Option<Bytes>, LLError> {
let key: Vec<Vec<u8>> = path.iter().map(|c| c.to_vec()).collect();
Ok(self.data.get(&key).cloned())
}
}
impl LLWriter for SyncStore {
fn ll_write(&mut self, path: &[&[u8]], data: Bytes) -> Result<LLPath, LLError> {
let key: Vec<Vec<u8>> = path.iter().map(|c| c.to_vec()).collect();
self.data.insert(key, data);
Ok(path.iter().map(|c| Bytes::copy_from_slice(c)).collect())
}
}
let sync_store = SyncStore {
data: HashMap::new(),
};
let mut async_store = SyncToAsyncLL::new(sync_store);
async_store
.ll_write_async(&[b"key"], Bytes::from_static(b"value"))
.await
.unwrap();
let result = async_store.ll_read_async(&[b"key"]).await.unwrap();
assert_eq!(result, Some(Bytes::from_static(b"value")));
}
}