oxios_kernel/image_gen/
store.rs1use std::path::PathBuf;
9
10use crate::image_gen::ImageGenError;
11
12pub trait ImageSink: Send + Sync {
14 fn save(&self, bytes: Vec<u8>, ext: &str) -> Result<String, ImageGenError>;
16}
17
18pub struct FsImageStore {
23 dir: PathBuf,
24 serve_prefix: String,
25}
26
27impl FsImageStore {
28 pub fn new(dir: PathBuf, serve_prefix: String) -> Self {
30 Self { dir, serve_prefix }
31 }
32}
33
34impl ImageSink for FsImageStore {
35 fn save(&self, bytes: Vec<u8>, ext: &str) -> Result<String, ImageGenError> {
36 std::fs::create_dir_all(&self.dir).map_err(|e| ImageGenError::Store(e.to_string()))?;
37 let name = format!("{}.{ext}", uuid::Uuid::new_v4());
38 std::fs::write(self.dir.join(&name), &bytes)
39 .map_err(|e| ImageGenError::Store(e.to_string()))?;
40 Ok(format!("{}{name}", self.serve_prefix))
41 }
42}
43
44#[cfg(test)]
45mod tests {
46 use super::*;
47
48 #[test]
49 fn save_writes_file_and_returns_prefixed_url() {
50 let tmp = tempfile::tempdir().unwrap();
51 let store = FsImageStore::new(tmp.path().to_path_buf(), "/api/images/".into());
52 let url = store.save(vec![1, 2, 3, 4], "png").unwrap();
53 assert!(url.starts_with("/api/images/"));
54 assert!(url.ends_with(".png"));
55 let name = url.strip_prefix("/api/images/").unwrap();
57 let written = std::fs::read(tmp.path().join(name)).unwrap();
58 assert_eq!(written, vec![1, 2, 3, 4]);
59 }
60
61 #[test]
62 fn save_creates_missing_dir() {
63 let tmp = tempfile::tempdir().unwrap();
64 let nested = tmp.path().join("deeply").join("nested");
65 let store = FsImageStore::new(nested, "/api/images/".into());
66 let url = store.save(vec![0], "png").unwrap();
67 assert!(url.starts_with("/api/images/"));
68 }
69}