pub mod disk; pub mod memory; pub mod mixed; use std::pin::Pin; use async_trait::async_trait; use tokio_stream::Stream; use tokio_util::bytes::Bytes; use crate::{ config::{StorageConfig, StorageStrategyConfig}, crypt, }; use self::{disk::DiskStorage, memory::MemoryStorage, mixed::MixedStorage}; #[async_trait] pub trait Storage { async fn init(&mut self) -> anyhow::Result<()>; async fn eligible(&self, src: String) -> bool; async fn delete(&mut self, key: String) -> anyhow::Result<()>; async fn retrieve( &self, key: String, ) -> Option> + Send>>>; async fn save( &mut self, key: String, stream: Pin> + Send>>, ) -> anyhow::Result<()>; } pub struct StoragePool { storages: Vec>, } impl StoragePool { pub fn from_config(items: Vec) -> Self { Self { storages: items.into_iter().map(create_storage).collect(), } } pub async fn init(&mut self) -> anyhow::Result<()> { for item in &mut self.storages { item.init().await?; } Ok(()) } pub async fn retrieve( &self, src: String, ) -> Option> + Send>>> { let mut stream = None; let key = crypt::compute_key(src); for item in &self.storages { if let Some(storage_stream) = item.retrieve(key.clone()).await { stream = Some(storage_stream); break; } } stream } pub async fn save( &mut self, src: String, stream: Pin> + Send>>, ) -> anyhow::Result<()> { let key = crypt::compute_key(src); for item in self.storages.iter_mut() { if item.eligible(key.clone()).await { item.save(key, stream).await?; break; } } Ok(()) } } fn create_storage(item: StorageConfig) -> Box { match item.strategy { StorageStrategyConfig::Memory(config) => Box::new(MemoryStorage::new(config)), StorageStrategyConfig::Disk(config) => Box::new(DiskStorage::new(config)), StorageStrategyConfig::Mixed(config) => Box::new(MixedStorage::new(config)), } }