use std::{ error::Error, path::{Path, PathBuf}, sync::Arc, }; use tokio::io; pub trait FileStore { type Err: Clone + Send + Error; fn is_stored( &self, name: &str, ) -> impl std::future::Future> + std::marker::Send; fn store( &self, name: &str, data: &[u8], ) -> impl std::future::Future> + std::marker::Send; fn delete( &self, name: &str, ) -> impl std::future::Future> + std::marker::Send; } #[derive(Clone, Debug)] pub struct Files { root: PathBuf, } impl Files { pub fn new(root: impl AsRef) -> Self { let root = root.as_ref(); let root = root.into(); Self { root } } pub fn root(&self) -> &Path { &self.root } } impl FileStore for Files { type Err = Arc; async fn is_stored(&self, name: &str) -> Result { tracing::debug!("checking if {} is stored", name); // TODO: is this secure ;-; let name = name.replace("/", "").replace(".", ""); let res = tokio::fs::try_exists(self.root.join(name)) .await .map_err(|err| Arc::new(err)); tracing::debug!("file check res: {:?}", res); res } async fn store(&self, name: &str, data: &[u8]) -> Result<(), Self::Err> { tracing::debug!("storing {} is stored", name); let name = name.replace("/", "").replace(".", ""); let res = tokio::fs::write(self.root.join(name), data) .await .map_err(|err| Arc::new(err)); tracing::debug!("file store res: {:?}", res); res } async fn delete(&self, name: &str) -> Result<(), Self::Err> { tracing::debug!("deleting {}", name); let name = name.replace("/", "").replace(".", ""); let res = tokio::fs::remove_file(self.root.join(name)) .await .map_err(|err| Arc::new(err)); tracing::debug!("file delete res: {:?}", res); res } }