use super::{StorageError, StorageResult};
use futures_util::{StreamExt, stream::BoxStream};
use serde::{Serialize, de::DeserializeOwned};
#[derive(Debug, Clone, Default)]
#[non_exhaustive]
#[must_use = "list options do nothing unless passed to Dataset::list"]
pub struct ListOptions {
pub offset: u64,
pub limit: Option<u64>,
pub desc: bool,
}
#[derive(Debug, Clone)]
pub struct Page<T> {
pub items: Vec<T>,
pub total: u64,
pub offset: u64,
pub limit: Option<u64>,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct DatasetInfo {
pub name: String,
pub item_count: u64,
pub created_at: time::OffsetDateTime,
pub modified_at: time::OffsetDateTime,
}
impl DatasetInfo {
pub fn new(
name: String,
item_count: u64,
created_at: time::OffsetDateTime,
modified_at: time::OffsetDateTime,
) -> Self {
Self {
name,
item_count,
created_at,
modified_at,
}
}
}
#[async_trait::async_trait]
pub trait Dataset: Send + Sync {
async fn push_json(&self, item: serde_json::Value) -> StorageResult<()>;
async fn push_json_batch(&self, items: Vec<serde_json::Value>) -> StorageResult<()>;
async fn list_raw(&self, opts: ListOptions) -> StorageResult<Page<serde_json::Value>>;
fn stream_raw(&self, opts: ListOptions) -> BoxStream<'_, StorageResult<serde_json::Value>>;
async fn export_json(&self, path: &std::path::Path) -> StorageResult<()>;
async fn export_csv(&self, path: &std::path::Path) -> StorageResult<()>;
async fn info(&self) -> StorageResult<DatasetInfo>;
}
#[async_trait::async_trait]
pub trait DatasetExt: Dataset {
async fn push<T: Serialize + Send + Sync>(&self, item: &T) -> StorageResult<()> {
self.push_json(serde_json::to_value(item)?).await
}
async fn push_batch<T: Serialize + Send + Sync>(&self, items: &[T]) -> StorageResult<()> {
let items = items
.iter()
.map(serde_json::to_value)
.collect::<Result<Vec<_>, _>>()?;
self.push_json_batch(items).await
}
async fn list<T: DeserializeOwned>(&self, opts: ListOptions) -> StorageResult<Page<T>> {
let page = self.list_raw(opts).await?;
Ok(Page {
items: page
.items
.into_iter()
.map(serde_json::from_value)
.collect::<Result<Vec<_>, _>>()?,
total: page.total,
offset: page.offset,
limit: page.limit,
})
}
fn stream<T: DeserializeOwned + Send + 'static>(
&self,
opts: ListOptions,
) -> BoxStream<'_, StorageResult<T>> {
Box::pin(self.stream_raw(opts).map(|result| {
result.and_then(|value| serde_json::from_value(value).map_err(StorageError::from))
}))
}
}
impl<D: Dataset + ?Sized> DatasetExt for D {}