use image::imageops::FilterType; use std::fs::remove_file; use std::path::{Path, PathBuf}; use super::error::{Error, Result}; #[derive(Clone, Debug, PartialEq)] pub struct Thumbnail { pub id: String, pub root: PathBuf, } impl Thumbnail { pub fn open(id: &str, root: &Path) -> Result { 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_) } pub fn from_path(path: &Path) -> Result { let id = path .file_name() .map(|s| String::from(s.to_string_lossy())) .ok_or(Error::NotAnImage(PathBuf::from(path)))?; let root = path .parent() .ok_or(Error::FileNotFound(PathBuf::from(path)))?; 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 } pub fn stream(&self) -> Result { 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 { Error::FileNotFound(thumbnail_path) } else { Error::from(err) } }) } pub fn delete(&self) -> Result<()> { let path = Thumbnail::thumbnail_path(&self.id, &self.root); remove_file(path).map_err(Error::from) } } #[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()); } }