74 lines
2.2 KiB
Rust
74 lines
2.2 KiB
Rust
|
use std::{
|
||
|
env, io,
|
||
|
path::{Path, PathBuf},
|
||
|
sync::Arc,
|
||
|
};
|
||
|
|
||
|
use tokio::fs;
|
||
|
use uuid::Uuid;
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub struct FileStorage(Arc<Path>);
|
||
|
|
||
|
impl FileStorage {
|
||
|
pub fn new() -> anyhow::Result<Self> {
|
||
|
let var = env::var("DRIVE_STORAGE_PATH");
|
||
|
let path_str = match var {
|
||
|
Ok(ref string) => string,
|
||
|
Err(err) => {
|
||
|
tracing::info!(
|
||
|
%err,
|
||
|
"Error getting DRIVE_STORAGE_PATH variable. Defaulting to ./files"
|
||
|
);
|
||
|
"./files"
|
||
|
}
|
||
|
};
|
||
|
let path = Path::new(path_str);
|
||
|
match path.metadata() {
|
||
|
Ok(meta) => anyhow::ensure!(meta.is_dir(), "Expected path to a directory"),
|
||
|
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||
|
std::fs::create_dir_all(path)?;
|
||
|
}
|
||
|
Err(err) => return Err(err.into()),
|
||
|
};
|
||
|
Ok(FileStorage(path.into()))
|
||
|
}
|
||
|
|
||
|
fn path_for_file(&self, file_id: Uuid) -> PathBuf {
|
||
|
let file_name = file_id.as_hyphenated().to_string();
|
||
|
self.0.join(file_name)
|
||
|
}
|
||
|
|
||
|
async fn create_inner(&self, file_id: Uuid) -> anyhow::Result<impl tokio::io::AsyncWrite> {
|
||
|
fs::File::create_new(self.path_for_file(file_id))
|
||
|
.await
|
||
|
.map_err(Into::into)
|
||
|
}
|
||
|
|
||
|
pub async fn create(&self) -> anyhow::Result<(Uuid, impl tokio::io::AsyncWrite)> {
|
||
|
let mut error = anyhow::anyhow!("Error creating a file");
|
||
|
for _ in 0..3 {
|
||
|
let file_id = Uuid::new_v4();
|
||
|
match self.create_inner(file_id).await {
|
||
|
Ok(file) => return Ok((file_id, file)),
|
||
|
Err(err) => error = error.context(err),
|
||
|
}
|
||
|
}
|
||
|
Err(error)
|
||
|
}
|
||
|
|
||
|
pub async fn read(&self, file_id: Uuid) -> anyhow::Result<impl tokio::io::AsyncRead> {
|
||
|
fs::File::open(self.path_for_file(file_id))
|
||
|
.await
|
||
|
.map_err(Into::into)
|
||
|
}
|
||
|
|
||
|
pub async fn delete(&self, file_id: Uuid) -> anyhow::Result<bool> {
|
||
|
match fs::remove_file(self.path_for_file(file_id)).await {
|
||
|
Ok(()) => Ok(true),
|
||
|
Err(err) if err.kind() == io::ErrorKind::NotFound => Ok(false),
|
||
|
Err(err) => Err(err.into()),
|
||
|
}
|
||
|
}
|
||
|
}
|