use async_trait::async_trait;
use bytes::Bytes;
use serde::{Deserialize, Serialize};
use crate::error::PodError;
#[cfg(feature = "fs-backend")]
pub mod fs;
#[cfg(feature = "memory-backend")]
pub mod memory;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResourceMeta {
pub etag: String,
pub modified: chrono::DateTime<chrono::Utc>,
pub size: u64,
pub content_type: String,
pub links: Vec<String>,
}
impl ResourceMeta {
pub fn new(etag: impl Into<String>, size: u64, content_type: impl Into<String>) -> Self {
ResourceMeta {
etag: etag.into(),
modified: chrono::Utc::now(),
size,
content_type: content_type.into(),
links: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum StorageEvent {
Created(String),
Updated(String),
Deleted(String),
}
#[async_trait]
pub trait Storage: Send + Sync + 'static {
async fn get(&self, path: &str) -> Result<(Bytes, ResourceMeta), PodError>;
async fn put(
&self,
path: &str,
body: Bytes,
content_type: &str,
) -> Result<ResourceMeta, PodError>;
async fn delete(&self, path: &str) -> Result<(), PodError>;
async fn list(&self, container: &str) -> Result<Vec<String>, PodError>;
async fn head(&self, path: &str) -> Result<ResourceMeta, PodError>;
async fn exists(&self, path: &str) -> Result<bool, PodError>;
async fn create_container(&self, path: &str) -> Result<ResourceMeta, PodError> {
let container = if path.ends_with('/') {
path.to_string()
} else {
format!("{path}/")
};
let meta_path = format!("{container}.meta", container = container.trim_end_matches('/'));
self.put(
&meta_path,
Bytes::new(),
"application/ld+json",
)
.await
}
async fn watch(
&self,
path: &str,
) -> Result<tokio::sync::mpsc::Receiver<StorageEvent>, PodError>;
}