2023-09-19 22:55:53 +00:00
|
|
|
use image::imageops::FilterType;
|
|
|
|
use std::fs::remove_file;
|
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2023-09-21 03:06:34 +00:00
|
|
|
use crate::FileError;
|
2023-09-19 22:55:53 +00:00
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
|
|
pub struct Thumbnail {
|
|
|
|
pub id: String,
|
|
|
|
pub root: PathBuf,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Thumbnail {
|
2023-09-21 03:06:34 +00:00
|
|
|
pub fn open(id: &str, root: &Path) -> Result<Thumbnail, FileError> {
|
2023-09-19 22:55:53 +00:00
|
|
|
let mut source_path = PathBuf::from(root);
|
|
|
|
source_path.push(id);
|
|
|
|
|
|
|
|
let self_ = Thumbnail {
|
|
|
|
id: String::from(id),
|
|
|
|
root: PathBuf::from(root),
|
|
|
|
};
|
|
|
|
|
|
|
|
let thumbnail_path = Thumbnail::thumbnail_path(id, root);
|
|
|
|
if !thumbnail_path.exists() {
|
|
|
|
let img = image::open(source_path)?;
|
|
|
|
let tn = img.resize(640, 640, FilterType::Nearest);
|
|
|
|
tn.save(thumbnail_path)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(self_)
|
|
|
|
}
|
|
|
|
|
2023-09-21 03:06:34 +00:00
|
|
|
pub fn from_path(path: &Path) -> Result<Thumbnail, FileError> {
|
2023-09-19 22:55:53 +00:00
|
|
|
let id = path
|
|
|
|
.file_name()
|
|
|
|
.map(|s| String::from(s.to_string_lossy()))
|
2023-09-21 03:06:34 +00:00
|
|
|
.ok_or(FileError::NotAnImage(PathBuf::from(path)))?;
|
2023-09-19 22:55:53 +00:00
|
|
|
|
|
|
|
let root = path
|
|
|
|
.parent()
|
2023-09-21 03:06:34 +00:00
|
|
|
.ok_or(FileError::FileNotFound(PathBuf::from(path)))?;
|
2023-09-19 22:55:53 +00:00
|
|
|
|
|
|
|
Thumbnail::open(&id, root)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn thumbnail_path(id: &str, root: &Path) -> PathBuf {
|
|
|
|
let mut path = PathBuf::from(root);
|
|
|
|
path.push(".thumbnails");
|
|
|
|
path.push(id.clone());
|
|
|
|
path
|
|
|
|
}
|
|
|
|
|
2023-09-21 03:06:34 +00:00
|
|
|
pub fn stream(&self) -> Result<std::fs::File, FileError> {
|
2023-09-19 22:55:53 +00:00
|
|
|
let thumbnail_path = Thumbnail::thumbnail_path(&self.id, &self.root);
|
|
|
|
std::fs::File::open(thumbnail_path.clone()).map_err(|err| {
|
|
|
|
if err.kind() == std::io::ErrorKind::NotFound {
|
2023-09-21 03:06:34 +00:00
|
|
|
FileError::FileNotFound(thumbnail_path)
|
2023-09-19 22:55:53 +00:00
|
|
|
} else {
|
2023-09-21 03:06:34 +00:00
|
|
|
FileError::from(err)
|
2023-09-19 22:55:53 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-09-21 03:06:34 +00:00
|
|
|
pub fn delete(&self) -> Result<(), FileError> {
|
2023-09-19 22:55:53 +00:00
|
|
|
let path = Thumbnail::thumbnail_path(&self.id, &self.root);
|
2023-09-21 03:06:34 +00:00
|
|
|
remove_file(path).map_err(FileError::from)
|
2023-09-19 22:55:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
use crate::lib::utils::FileCleanup;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn it_creates_a_thumbnail_if_one_does_not_exist() {
|
|
|
|
let _ = FileCleanup(PathBuf::from("fixtures/.thumbnails/rawr.png"));
|
|
|
|
let _ =
|
|
|
|
Thumbnail::open("rawr.png", Path::new("fixtures")).expect("thumbnail open must work");
|
|
|
|
assert!(Path::new("fixtures/.thumbnails/rawr.png").is_file());
|
|
|
|
}
|
|
|
|
}
|