use async_trait::async_trait;
use crate::{Error, Path, Record};
#[async_trait]
pub trait AsyncReader: Send + Sync {
async fn read_async(&mut self, from: &Path) -> Result<Option<Record>, Error>;
}
#[async_trait]
pub trait AsyncWriter: Send + Sync {
async fn write_async(&mut self, to: &Path, data: Record) -> Result<Path, Error>;
}
pub trait AsyncStore: AsyncReader + AsyncWriter {}
impl<T: AsyncReader + AsyncWriter> AsyncStore for T {}
#[async_trait]
impl<T: AsyncReader + ?Sized> AsyncReader for &mut T {
async fn read_async(&mut self, from: &Path) -> Result<Option<Record>, Error> {
(*self).read_async(from).await
}
}
#[async_trait]
impl<T: AsyncWriter + ?Sized> AsyncWriter for &mut T {
async fn write_async(&mut self, to: &Path, data: Record) -> Result<Path, Error> {
(*self).write_async(to, data).await
}
}
#[async_trait]
impl<T: AsyncReader + ?Sized> AsyncReader for Box<T> {
async fn read_async(&mut self, from: &Path) -> Result<Option<Record>, Error> {
self.as_mut().read_async(from).await
}
}
#[async_trait]
impl<T: AsyncWriter + ?Sized> AsyncWriter for Box<T> {
async fn write_async(&mut self, to: &Path, data: Record) -> Result<Path, Error> {
self.as_mut().write_async(to, data).await
}
}
pub struct SyncToAsync<T> {
inner: std::sync::Arc<std::sync::Mutex<T>>,
}
impl<T> SyncToAsync<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 SyncToAsync<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
#[async_trait]
impl<T: crate::Reader + Send + 'static> AsyncReader for SyncToAsync<T> {
async fn read_async(&mut self, from: &Path) -> Result<Option<Record>, Error> {
let path = from.clone();
let inner = self.inner.clone();
let mut guard = inner
.lock()
.map_err(|_| Error::store("sync_to_async", "read", "lock poisoned"))?;
guard.read(&path)
}
}
#[async_trait]
impl<T: crate::Writer + Send + 'static> AsyncWriter for SyncToAsync<T> {
async fn write_async(&mut self, to: &Path, data: Record) -> Result<Path, Error> {
let path = to.clone();
let inner = self.inner.clone();
let mut guard = inner
.lock()
.map_err(|_| Error::store("sync_to_async", "write", "lock poisoned"))?;
guard.write(&path, data)
}
}
pub type DetachedFuture<T> =
std::pin::Pin<Box<dyn std::future::Future<Output = Result<T, Error>> + Send + 'static>>;
pub trait DetachedReader: Send {
fn read_detached(&mut self, from: &Path) -> DetachedFuture<Option<Record>>;
}
pub trait DetachedWriter: Send {
fn write_detached(&mut self, to: &Path, data: Record) -> DetachedFuture<Path>;
}
pub trait DetachedStore: DetachedReader + DetachedWriter {}
impl<T: DetachedReader + DetachedWriter> DetachedStore for T {}
impl<T: DetachedReader + ?Sized> DetachedReader for &mut T {
fn read_detached(&mut self, from: &Path) -> DetachedFuture<Option<Record>> {
(*self).read_detached(from)
}
}
impl<T: DetachedWriter + ?Sized> DetachedWriter for &mut T {
fn write_detached(&mut self, to: &Path, data: Record) -> DetachedFuture<Path> {
(*self).write_detached(to, data)
}
}
impl<T: DetachedReader + ?Sized> DetachedReader for Box<T> {
fn read_detached(&mut self, from: &Path) -> DetachedFuture<Option<Record>> {
self.as_mut().read_detached(from)
}
}
impl<T: DetachedWriter + ?Sized> DetachedWriter for Box<T> {
fn write_detached(&mut self, to: &Path, data: Record) -> DetachedFuture<Path> {
self.as_mut().write_detached(to, data)
}
}
impl<S: crate::Reader + 'static> DetachedReader for crate::Shared<S> {
fn read_detached(&mut self, from: &Path) -> DetachedFuture<Option<Record>> {
let shared = self.clone();
let path = from.clone();
Box::pin(async move { shared.lock().read(&path) })
}
}
impl<S: crate::Writer + 'static> DetachedWriter for crate::Shared<S> {
fn write_detached(&mut self, to: &Path, data: Record) -> DetachedFuture<Path> {
let shared = self.clone();
let path = to.clone();
Box::pin(async move { shared.lock().write(&path, data) })
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{Format, Value};
use bytes::Bytes;
use std::collections::HashMap;
struct TestAsyncStore {
data: HashMap<Path, Record>,
}
impl TestAsyncStore {
fn new() -> Self {
Self {
data: HashMap::new(),
}
}
}
#[async_trait]
impl AsyncReader for TestAsyncStore {
async fn read_async(&mut self, from: &Path) -> Result<Option<Record>, Error> {
Ok(self.data.get(from).cloned())
}
}
#[async_trait]
impl AsyncWriter for TestAsyncStore {
async fn write_async(&mut self, to: &Path, data: Record) -> Result<Path, Error> {
self.data.insert(to.clone(), data);
Ok(to.clone())
}
}
#[tokio::test]
async fn async_read_write_works() {
use crate::path;
let mut store = TestAsyncStore::new();
let record = Record::raw(Bytes::from_static(b"hello"), Format::JSON);
store
.write_async(&path!("users/123"), record)
.await
.unwrap();
let result = store.read_async(&path!("users/123")).await.unwrap();
assert!(result.is_some());
let result = store.read_async(&path!("nonexistent")).await.unwrap();
assert!(result.is_none());
}
#[tokio::test]
async fn async_with_parsed_values() {
use crate::path;
let mut store = TestAsyncStore::new();
let value = Value::from("hello world");
store
.write_async(&path!("data/greeting"), Record::parsed(value))
.await
.unwrap();
let result = store.read_async(&path!("data/greeting")).await.unwrap();
assert!(result.is_some());
let record = result.unwrap();
assert!(record.is_parsed());
assert_eq!(record.as_value(), Some(&Value::from("hello world")));
}
#[tokio::test]
async fn object_safety_works() {
use crate::path;
let mut store = TestAsyncStore::new();
let boxed: &mut dyn AsyncStore = &mut store;
boxed
.write_async(
&path!("test"),
Record::raw(Bytes::from_static(b"data"), Format::OCTET_STREAM),
)
.await
.unwrap();
let result = boxed.read_async(&path!("test")).await.unwrap();
assert!(result.is_some());
}
#[tokio::test]
async fn detached_futures_do_not_borrow_the_store() {
use crate::{path, MemoryStore, Shared};
let mut store = Shared::new(MemoryStore::new());
let write_fut = store.write_detached(&path!("key"), Record::parsed(Value::from("v")));
write_fut.await.unwrap();
let read_fut = store.read_detached(&path!("key"));
let another_read = store.read_detached(&path!("key"));
let (a, b) = (read_fut.await.unwrap(), another_read.await.unwrap());
assert!(a.is_some());
assert!(b.is_some());
}
#[tokio::test]
async fn detached_object_safety() {
use crate::{path, MemoryStore, Shared};
let mut boxed: Box<dyn DetachedStore> = Box::new(Shared::new(MemoryStore::new()));
boxed
.write_detached(&path!("k"), Record::parsed(Value::from(1i64)))
.await
.unwrap();
assert!(boxed.read_detached(&path!("k")).await.unwrap().is_some());
}
#[tokio::test]
async fn sync_to_async_adapter_works() {
use crate::{path, Reader, Writer};
struct SyncStore {
data: HashMap<Path, Record>,
}
impl Reader for SyncStore {
fn read(&mut self, from: &Path) -> Result<Option<Record>, Error> {
Ok(self.data.get(from).cloned())
}
}
impl Writer for SyncStore {
fn write(&mut self, to: &Path, data: Record) -> Result<Path, Error> {
self.data.insert(to.clone(), data);
Ok(to.clone())
}
}
let sync_store = SyncStore {
data: HashMap::new(),
};
let mut async_store = SyncToAsync::new(sync_store);
async_store
.write_async(
&path!("key"),
Record::raw(Bytes::from_static(b"value"), Format::OCTET_STREAM),
)
.await
.unwrap();
let result = async_store.read_async(&path!("key")).await.unwrap();
assert!(result.is_some());
}
}