pub mod factory;
pub mod local;
pub mod s3;
use std::pin::Pin;
use async_trait::async_trait;
use bytes::Bytes;
use futures::Stream;
use serde::Serialize;
use crate::error::AppError;
pub type ByteStream = Pin<Box<dyn Stream<Item = Result<Bytes, std::io::Error>> + Send>>;
#[derive(Debug, Clone, Default)]
pub struct GetOptions {
pub range: Option<String>,
}
pub struct StorageResponse {
pub body: ByteStream,
pub content_length: Option<u64>,
pub content_type: Option<String>,
pub etag: Option<String>,
pub last_modified: Option<String>,
pub content_range: Option<String>,
pub is_partial: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct FileMeta {
pub path: String,
pub size: u64,
pub etag: Option<String>,
pub content_type: Option<String>,
pub last_modified: Option<String>,
pub is_dir: bool,
}
#[derive(Debug, Clone, Serialize)]
pub struct FileEntry {
pub key: String,
pub size: u64,
pub last_modified: Option<String>,
pub is_dir: bool,
pub is_symlink: bool,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct ListResult {
pub entries: Vec<FileEntry>,
pub next_token: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub walked_tokens: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub total_pages: Option<u64>,
}
#[async_trait]
pub trait StorageBackend: Send + Sync {
async fn get_file(&self, path: &str, opts: GetOptions) -> Result<StorageResponse, AppError>;
async fn list_files(&self, prefix: &str, token: Option<String>) -> Result<ListResult, AppError>;
async fn stat(&self, path: &str) -> Result<FileMeta, AppError>;
async fn list_files_walking(
&self,
prefix: &str,
token: Option<String>,
skip: u32,
) -> Result<ListResult, AppError> {
let mut walked: Vec<String> = Vec::with_capacity(skip as usize);
let mut current = token;
for _ in 0..skip {
let step = self.list_files(prefix, current).await?;
match step.next_token {
Some(t) => {
walked.push(t.clone());
current = Some(t);
}
None => {
return Ok(ListResult {
entries: step.entries,
next_token: None,
walked_tokens: walked,
total_pages: step.total_pages,
});
}
}
}
let mut result = self.list_files(prefix, current).await?;
result.walked_tokens = walked;
Ok(result)
}
}