mod contents;
pub mod drivers;
pub mod strategies;
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};
use bytes::Bytes;
use self::drivers::StoreDriver;
#[derive(thiserror::Error, Debug)]
#[allow(clippy::module_name_repetitions)]
pub enum StorageError {
#[error("store not found by the given key: {0}")]
StoreNotFound(String),
#[error(transparent)]
Store(#[from] object_store::Error),
#[error("Unable to read data from file {}", path.display().to_string())]
UnableToReadBytes { path: PathBuf },
#[error("secondaries errors")]
Multi(BTreeMap<String, String>),
#[error(transparent)]
Any(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub type StorageResult<T> = std::result::Result<T, StorageError>;
pub struct Storage {
pub stores: BTreeMap<String, Box<dyn StoreDriver>>,
pub strategy: Box<dyn strategies::StorageStrategy>,
}
impl Storage {
#[must_use]
pub fn single(store: Box<dyn StoreDriver>) -> Self {
let default_key = "store";
Self {
strategy: Box::new(strategies::single::SingleStrategy::new(default_key)),
stores: BTreeMap::from([(default_key.to_string(), store)]),
}
}
#[must_use]
pub fn new(
stores: BTreeMap<String, Box<dyn StoreDriver>>,
strategy: Box<dyn strategies::StorageStrategy>,
) -> Self {
Self { stores, strategy }
}
pub async fn upload(&self, path: &Path, content: &Bytes) -> StorageResult<()> {
self.upload_with_strategy(path, content, &*self.strategy)
.await
}
pub async fn upload_with_strategy(
&self,
path: &Path,
content: &Bytes,
strategy: &dyn strategies::StorageStrategy,
) -> StorageResult<()> {
strategy.upload(self, path, content).await
}
pub async fn download<T: TryFrom<contents::Contents>>(&self, path: &Path) -> StorageResult<T> {
self.download_with_policy(path, &*self.strategy).await
}
pub async fn download_with_policy<T: TryFrom<contents::Contents>>(
&self,
path: &Path,
strategy: &dyn strategies::StorageStrategy,
) -> StorageResult<T> {
let res = strategy.download(self, path).await?;
contents::Contents::from(res).try_into().map_or_else(
|_| {
Err(StorageError::UnableToReadBytes {
path: path.to_path_buf(),
})
},
|content| Ok(content),
)
}
pub async fn delete(&self, path: &Path) -> StorageResult<()> {
self.delete_with_policy(path, &*self.strategy).await
}
pub async fn delete_with_policy(
&self,
path: &Path,
strategy: &dyn strategies::StorageStrategy,
) -> StorageResult<()> {
strategy.delete(self, path).await
}
pub async fn rename(&self, from: &Path, to: &Path) -> StorageResult<()> {
self.rename_with_policy(from, to, &*self.strategy).await
}
pub async fn rename_with_policy(
&self,
from: &Path,
to: &Path,
strategy: &dyn strategies::StorageStrategy,
) -> StorageResult<()> {
strategy.rename(self, from, to).await
}
pub async fn copy(&self, from: &Path, to: &Path) -> StorageResult<()> {
self.copy_with_policy(from, to, &*self.strategy).await
}
pub async fn copy_with_policy(
&self,
from: &Path,
to: &Path,
strategy: &dyn strategies::StorageStrategy,
) -> StorageResult<()> {
strategy.copy(self, from, to).await
}
#[must_use]
pub fn as_store(&self, name: &str) -> Option<&dyn StoreDriver> {
self.stores.get(name).map(|s| &**s)
}
pub fn as_store_err(&self, name: &str) -> StorageResult<&dyn StoreDriver> {
self.as_store(name)
.ok_or(StorageError::StoreNotFound(name.to_string()))
}
}