picox/src/storage_manager/file_store.rs

104 lines
2.8 KiB
Rust

use crate::model::image::{Image, ImageData, StorageLocation};
use crate::storage_manager::{Store, StoreError};
use async_trait::async_trait;
use j_db::database::Database;
use serde::{Deserialize, Serialize};
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::hash::Hasher;
use std::os::unix::fs::MetadataExt;
use std::path::PathBuf;
use url::Url;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileStoreConfig {
pub base_path: PathBuf,
pub base_url: String,
pub max_file_size: usize,
pub max_total_storage: usize,
}
#[derive(Debug, Clone)]
pub struct FileStore {
pub config: FileStoreConfig,
}
impl FileStore {
pub fn new(config: FileStoreConfig) -> Self {
Self { config }
}
}
#[async_trait]
impl Store for FileStore {
async fn store_img(
&mut self,
img_data: ImageData,
file_name: &str,
) -> Result<(Url, StorageLocation), StoreError> {
let img_data = match img_data {
ImageData::Bytes(b) => b,
ImageData::Link(_) => unimplemented!("No link support"),
};
let mut hasher = DefaultHasher::new();
hasher.write(img_data.as_slice());
let disk_file_name = hex::encode(hasher.finish().to_be_bytes());
let file = PathBuf::from(file_name);
let ext = file.extension().unwrap().to_str().unwrap();
let disk_file_name = format!("{}.{}", disk_file_name, ext);
let path = self.config.base_path.join(&disk_file_name);
tokio::fs::write(&path, img_data).await?;
let img_link = Url::parse(&self.config.base_url)
.unwrap()
.join(&disk_file_name)
.unwrap();
let storage_location = StorageLocation::FileStore { path };
Ok((img_link, storage_location))
}
async fn delete_img(&mut self, img: Image) -> Result<(), StoreError> {
match img.storage_location {
StorageLocation::FileStore { path } => {
tokio::fs::remove_file(path).await?;
}
StorageLocation::Link => unimplemented!("No link support"),
}
Ok(())
}
fn max_image_size(&self) -> usize {
self.config.max_file_size
}
fn max_total_storage(&self) -> usize {
self.config.max_total_storage
}
async fn current_store_size(&self, db: &Database) -> Result<usize, StoreError> {
let files: Vec<StorageLocation> = db
.filter(|_, _file: &Image| true)
.unwrap()
.map(|file: Image| file.storage_location.clone())
.collect();
let mut total_size = 0;
for file in files {
if let StorageLocation::FileStore { path } = file {
let md = fs::metadata(path)?;
total_size += md.size();
}
}
Ok(total_size as usize)
}
}