use std::path::PathBuf;
use crate::image_gen::ImageGenError;
pub trait ImageSink: Send + Sync {
fn save(&self, bytes: Vec<u8>, ext: &str) -> Result<String, ImageGenError>;
}
pub struct FsImageStore {
dir: PathBuf,
serve_prefix: String,
}
impl FsImageStore {
pub fn new(dir: PathBuf, serve_prefix: String) -> Self {
Self { dir, serve_prefix }
}
}
impl ImageSink for FsImageStore {
fn save(&self, bytes: Vec<u8>, ext: &str) -> Result<String, ImageGenError> {
std::fs::create_dir_all(&self.dir).map_err(|e| ImageGenError::Store(e.to_string()))?;
let name = format!("{}.{ext}", uuid::Uuid::new_v4());
std::fs::write(self.dir.join(&name), &bytes)
.map_err(|e| ImageGenError::Store(e.to_string()))?;
Ok(format!("{}{name}", self.serve_prefix))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn save_writes_file_and_returns_prefixed_url() {
let tmp = tempfile::tempdir().unwrap();
let store = FsImageStore::new(tmp.path().to_path_buf(), "/api/images/".into());
let url = store.save(vec![1, 2, 3, 4], "png").unwrap();
assert!(url.starts_with("/api/images/"));
assert!(url.ends_with(".png"));
let name = url.strip_prefix("/api/images/").unwrap();
let written = std::fs::read(tmp.path().join(name)).unwrap();
assert_eq!(written, vec![1, 2, 3, 4]);
}
#[test]
fn save_creates_missing_dir() {
let tmp = tempfile::tempdir().unwrap();
let nested = tmp.path().join("deeply").join("nested");
let store = FsImageStore::new(nested, "/api/images/".into());
let url = store.save(vec![0], "png").unwrap();
assert!(url.starts_with("/api/images/"));
}
}