use std::{ collections::HashMap, convert::Infallible, error::Error, path::{Path, PathBuf}, sync::Arc, }; use tokio::io; use tokio::sync::Mutex; 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 FilesMem { files: Arc>>>, } impl FilesMem { pub fn new() -> Self { Self { files: Arc::new(Mutex::new(HashMap::new())), } } } impl FileStore for FilesMem { type Err = Infallible; async fn is_stored(&self, name: &str) -> Result { Ok(self.files.lock().await.contains_key(name)) } async fn store(&self, name: &str, data: &[u8]) -> Result<(), Self::Err> { self.files .lock() .await .insert(name.to_string(), data.to_owned()); Ok(()) } async fn delete(&self, name: &str) -> Result<(), Self::Err> { self.files.lock().await.remove(name); Ok(()) } } #[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug)] pub struct Files { root: PathBuf, } #[cfg(not(target_arch = "wasm32"))] 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 } } #[cfg(not(target_arch = "wasm32"))] 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 } }