use std::{io::Cursor, path::Path}; use anyhow::{bail, Context}; use image::{io::Reader as ImageReader, DynamicImage, ImageOutputFormat}; use crate::{load::Loadable, resp::Response, util::slugify}; pub struct MyImage { original: DynamicImage, thumbnail: DynamicImage, format: ImageOutputFormat, original_url: String, thumbnail_url: String, } impl MyImage { // (original, thumbnail) pub fn into_responses(self) -> anyhow::Result<(String, Response, String, Response)> { let content_type = format_to_content_type(&self.format)?; let mut original = vec![]; self.original .write_to(&mut Cursor::new(&mut original), self.format.clone())?; let mut thumbnail = vec![]; self.thumbnail .write_to(&mut Cursor::new(&mut thumbnail), self.format)?; Ok(( self.original_url, Response { content_type, data: original, }, self.thumbnail_url, Response { content_type, data: thumbnail, }, )) } } impl Loadable for MyImage { fn load(path: &Path, slug_prefix: &str) -> anyhow::Result<MyImage> { let file_name = path .file_name() .context("Could not get file name")? .to_str() .context("Could not convert filename into string")? .to_owned(); let original_url = slugify(&format!("/img/{slug_prefix}/{file_name}")); let thumbnail_url = slugify(&format!("/thumb/{slug_prefix}/{file_name}")); let reader = ImageReader::open(path)?; let format = reader .format() .context("Could not determine image format")? .into(); let original = reader.decode()?; let thumbnail = original.thumbnail(400, 800); Ok(Self { original, thumbnail, format, original_url, thumbnail_url, }) } } fn format_to_content_type(f: &ImageOutputFormat) -> anyhow::Result<&'static str> { let s = match f { ImageOutputFormat::Png => "image/png", ImageOutputFormat::Jpeg(_) => "image/jpg", ImageOutputFormat::Bmp => "image/bmp", ImageOutputFormat::Gif => "image/gif", ImageOutputFormat::Ico => "image/x-icon", _ => bail!("Invalid content type {:?}", f), }; Ok(s) }