use futures_lite::StreamExt;
use serde::{de::DeserializeOwned, Serialize};
use std::pin::Pin;
use std::task::{Context, Poll};
pub use crate::core::errors::StorageError;
use crate::core::errors::StorageResult;
use foundation_core::valtron::Stream;
type AsyncStream<T> = Pin<Box<dyn futures_core::Stream<Item = T> + Send>>;
#[cfg(target_family = "wasm")]
pub type StorageItemStream<'a, T> =
Box<dyn Iterator<Item = Stream<Result<T, StorageError>, ()>> + 'a>;
#[cfg(not(target_family = "wasm"))]
pub type StorageItemStream<'a, T> =
Box<dyn Iterator<Item = Stream<Result<T, StorageError>, ()>> + Send + 'a>;
#[cfg(target_family = "wasm")]
pub type AsyncStorageItemStream<'a, T> =
Pin<Box<dyn futures_core::Stream<Item = StorageResult<T>> + 'a>>;
#[cfg(not(target_family = "wasm"))]
pub type AsyncStorageItemStream<'a, T> =
Pin<Box<dyn futures_core::Stream<Item = StorageResult<T>> + Send + 'a>>;
#[derive(Debug, Clone)]
pub enum DataValue {
Null,
Integer(i64),
Real(f64),
Text(String),
Blob(Vec<u8>),
}
#[derive(Debug, Clone)]
pub struct SqlRow {
columns: Vec<(String, DataValue)>,
}
impl SqlRow {
#[must_use]
pub fn new(columns: Vec<(String, DataValue)>) -> Self {
Self { columns }
}
pub fn get<T: FromDataValue>(&self, index: usize) -> StorageResult<T> {
self.columns
.get(index)
.ok_or_else(|| {
crate::core::errors::StorageError::SqlConversion(format!(
"Column index {index} out of bounds"
))
})
.and_then(|(_, v)| T::from_data_value(v))
}
pub fn get_by_name<T: FromDataValue>(&self, name: &str) -> StorageResult<T> {
self.columns
.iter()
.find(|(col_name, _)| col_name == name)
.ok_or_else(|| {
crate::core::errors::StorageError::SqlConversion(format!(
"Column '{name}' not found"
))
})
.and_then(|(_, v)| T::from_data_value(v))
}
#[must_use]
pub fn column_count(&self) -> usize {
self.columns.len()
}
}
pub trait FromDataValue: Sized {
fn from_data_value(value: &DataValue) -> StorageResult<Self>;
}
impl FromDataValue for String {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Text(s) => Ok(s.clone()),
DataValue::Null => Ok(String::new()),
_ => Err(crate::core::errors::StorageError::SqlConversion(
"Cannot convert to String".to_string(),
)),
}
}
}
impl FromDataValue for i64 {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Integer(i) => Ok(*i),
DataValue::Null => Ok(0),
_ => Err(crate::core::errors::StorageError::SqlConversion(
"Cannot convert to i64".to_string(),
)),
}
}
}
impl FromDataValue for i32 {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Integer(i) => {
#[allow(clippy::cast_possible_truncation)]
return Ok(*i as i32);
}
DataValue::Null => Ok(0),
_ => Err(crate::core::errors::StorageError::SqlConversion(
"Cannot convert to i32".to_string(),
)),
}
}
}
impl FromDataValue for f64 {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Real(r) => Ok(*r),
DataValue::Null => Ok(0.0),
_ => Err(crate::core::errors::StorageError::SqlConversion(
"Cannot convert to f64".to_string(),
)),
}
}
}
impl FromDataValue for Vec<u8> {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Blob(b) => Ok(b.clone()),
DataValue::Null => Ok(Vec::new()),
_ => Err(crate::core::errors::StorageError::SqlConversion(
"Cannot convert to Vec<u8>".to_string(),
)),
}
}
}
impl<T: FromDataValue> FromDataValue for Option<T> {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Null => Ok(None),
other => T::from_data_value(other).map(Some),
}
}
}
impl FromDataValue for bool {
fn from_data_value(value: &DataValue) -> StorageResult<Self> {
match value {
DataValue::Integer(i) => Ok(*i != 0),
DataValue::Null => Ok(false),
_ => Err(crate::core::errors::StorageError::SqlConversion(
"Cannot convert to bool".to_string(),
)),
}
}
}
pub struct AsyncQueryStream {
inner: AsyncStream<StorageResult<SqlRow>>,
}
impl AsyncQueryStream {
pub fn new<S>(stream: S) -> Self
where
S: futures_core::Stream<Item = StorageResult<SqlRow>> + Send + 'static,
{
Self {
inner: Box::pin(stream),
}
}
pub async fn collect_all(self) -> StorageResult<Vec<SqlRow>> {
self.inner.collect::<Vec<_>>().await.into_iter().collect()
}
}
impl futures_core::Stream for AsyncQueryStream {
type Item = StorageResult<SqlRow>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
Pin::new(&mut this.inner).poll_next(cx)
}
}
pub struct AsyncListStream {
inner: AsyncStream<StorageResult<String>>,
}
impl AsyncListStream {
pub fn new<S>(stream: S) -> Self
where
S: futures_core::Stream<Item = StorageResult<String>> + Send + 'static,
{
Self {
inner: Box::pin(stream),
}
}
pub async fn collect_all(self) -> StorageResult<Vec<String>> {
self.inner.collect::<Vec<_>>().await.into_iter().collect()
}
}
impl futures_core::Stream for AsyncListStream {
type Item = StorageResult<String>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
Pin::new(&mut this.inner).poll_next(cx)
}
}
pub struct AsyncQueryStreamIterator {
stream: AsyncQueryStream,
}
impl AsyncQueryStreamIterator {
#[must_use]
pub fn new(stream: AsyncQueryStream) -> Self {
Self { stream }
}
}
impl Iterator for AsyncQueryStreamIterator {
type Item = StorageResult<SqlRow>;
fn next(&mut self) -> Option<Self::Item> {
use futures_lite::future::block_on;
block_on(self.stream.next())
}
}
pub struct AsyncListStreamIterator {
stream: AsyncListStream,
}
impl AsyncListStreamIterator {
#[must_use]
pub fn new(stream: AsyncListStream) -> Self {
Self { stream }
}
}
impl Iterator for AsyncListStreamIterator {
type Item = StorageResult<String>;
fn next(&mut self) -> Option<Self::Item> {
use futures_lite::future::block_on;
block_on(self.stream.next())
}
}
pub trait KeyValueStore: Send + Sync {
fn get<V: DeserializeOwned + Send + 'static>(&self, key: &str) -> StorageResult<Option<V>>;
fn set<V: Serialize + Send + 'static>(&self, key: &str, value: V) -> StorageResult<()>;
fn delete(&self, key: &str) -> StorageResult<()>;
fn exists(&self, key: &str) -> StorageResult<bool>;
fn list_keys(&self, prefix: Option<&str>) -> StorageResult<StorageItemStream<'_, String>>;
}
pub trait BlobStore: Send + Sync {
fn put_blob(&self, key: &str, data: &[u8]) -> StorageResult<()>;
fn get_blob(&self, key: &str) -> StorageResult<Option<Vec<u8>>>;
fn delete_blob(&self, key: &str) -> StorageResult<()>;
fn blob_exists(&self, key: &str) -> StorageResult<bool>;
}
pub trait QueryStore: Send + Sync {
fn query(
&self,
sql: &str,
params: &[DataValue],
) -> StorageResult<StorageItemStream<'_, SqlRow>>;
fn execute(&self, sql: &str, params: &[DataValue]) -> StorageResult<u64>;
fn execute_batch(&self, sql: &str) -> StorageResult<()>;
}
#[async_trait::async_trait]
pub trait AsyncQueryStore: Send + Sync {
async fn query_async(&self, sql: &str, params: &[DataValue])
-> StorageResult<AsyncQueryStream>;
async fn execute_async(&self, sql: &str, params: &[DataValue]) -> StorageResult<u64>;
async fn execute_batch_async(&self, sql: &str) -> StorageResult<()>;
}
#[async_trait::async_trait]
pub trait AsyncKeyValueStore: Send + Sync {
async fn get_async<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
) -> StorageResult<Option<V>>;
async fn set_async<V: Serialize + Send + 'static>(
&self,
key: &str,
value: V,
) -> StorageResult<()>;
async fn delete_async(&self, key: &str) -> StorageResult<()>;
async fn exists_async(&self, key: &str) -> StorageResult<bool>;
async fn list_keys_async(&self, prefix: Option<&str>) -> StorageResult<AsyncListStream>;
}
pub trait RateLimiterStore: Send + Sync {
fn check_rate_limit(
&self,
key: &str,
max_count: u32,
window_seconds: u64,
) -> StorageResult<bool>;
fn record_rate_limit(&self, key: &str) -> StorageResult<u32>;
fn reset_rate_limit(&self, key: &str) -> StorageResult<()>;
}
#[async_trait::async_trait]
pub trait AsyncBlobStore: Send + Sync {
async fn put_blob_async(&self, key: &str, data: &[u8]) -> StorageResult<()>;
async fn get_blob_async(&self, key: &str) -> StorageResult<Option<Vec<u8>>>;
async fn delete_blob_async(&self, key: &str) -> StorageResult<()>;
async fn blob_exists_async(&self, key: &str) -> StorageResult<bool>;
}
#[async_trait::async_trait]
pub trait AsyncRateLimiterStore: Send + Sync {
async fn check_rate_limit_async(
&self,
key: &str,
max_count: u32,
window_seconds: u64,
) -> StorageResult<bool>;
async fn record_rate_limit_async(&self, key: &str) -> StorageResult<u32>;
async fn reset_rate_limit_async(&self, key: &str) -> StorageResult<()>;
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Document {
pub id: String,
pub content: String,
pub metadata: serde_json::Value,
pub title: Option<String>,
pub summary: Option<String>,
pub record_type: Option<String>,
}
impl Document {
#[must_use]
pub fn new(
id: String,
content: String,
metadata: serde_json::Value,
title: Option<String>,
summary: Option<String>,
record_type: Option<String>,
) -> Self {
Self {
id,
content,
metadata,
title,
summary,
record_type,
}
}
}
pub trait DocumentStore: Send + Sync {
fn append<V: Serialize + Send + 'static>(
&self,
key: &str,
content: V,
) -> StorageResult<Document>;
fn append_with_id<V: Serialize + Send + 'static>(
&self,
key: &str,
doc_id: &str,
content: V,
) -> StorageResult<Document>;
fn scan<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
limit: usize,
) -> StorageResult<StorageItemStream<'_, V>>;
fn scan_all<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
) -> StorageResult<StorageItemStream<'_, V>>;
fn scan_from<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
from_id: &str,
limit: usize,
) -> StorageResult<StorageItemStream<'_, V>>;
fn append_promotable<V: Serialize + PromotableDocument + Send + 'static>(
&self,
key: &str,
content: V,
) -> StorageResult<Document>;
fn append_promotable_with_id<V: Serialize + PromotableDocument + Send + 'static>(
&self,
key: &str,
doc_id: &str,
content: V,
) -> StorageResult<Document>;
fn scan_documents(&self, key: &str, limit: usize) -> StorageResult<Vec<Document>>;
fn scan_documents_from(
&self,
key: &str,
from_id: &str,
limit: usize,
) -> StorageResult<Vec<Document>>;
fn delete(&self, key: &str, doc_id: &str) -> StorageResult<()>;
fn delete_all(&self, key: &str) -> StorageResult<u64>;
fn count(&self, key: &str) -> StorageResult<u64>;
}
pub trait PromotableDocument {
fn record_type(&self) -> Option<String> {
None
}
fn title(&self) -> Option<String> {
None
}
fn summary(&self) -> Option<String> {
None
}
}
#[async_trait::async_trait]
pub trait AsyncDocumentStore: Send + Sync {
async fn append_async<V: Serialize + Send + 'static>(
&self,
key: &str,
content: V,
) -> StorageResult<Document>;
async fn append_with_id_async<V: Serialize + Send + 'static>(
&self,
key: &str,
doc_id: &str,
content: V,
) -> StorageResult<Document>;
async fn append_promotable_async<V: Serialize + PromotableDocument + Send + 'static>(
&self,
key: &str,
content: V,
) -> StorageResult<Document>;
async fn append_promotable_with_id_async<V: Serialize + PromotableDocument + Send + 'static>(
&self,
key: &str,
doc_id: &str,
content: V,
) -> StorageResult<Document>;
async fn scan_documents_async(&self, key: &str, limit: usize) -> StorageResult<Vec<Document>>;
async fn scan_documents_from_async(
&self,
key: &str,
from_id: &str,
limit: usize,
) -> StorageResult<Vec<Document>>;
async fn scan_async<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
limit: usize,
) -> StorageResult<AsyncStorageItemStream<'_, V>>;
async fn scan_all_async<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
) -> StorageResult<AsyncStorageItemStream<'_, V>>;
async fn scan_from_async<V: DeserializeOwned + Send + 'static>(
&self,
key: &str,
from_id: &str,
limit: usize,
) -> StorageResult<AsyncStorageItemStream<'_, V>>;
async fn delete_async(&self, key: &str, doc_id: &str) -> StorageResult<()>;
async fn delete_all_async(&self, key: &str) -> StorageResult<u64>;
async fn count_async(&self, key: &str) -> StorageResult<u64>;
}