use anyhow::{Context, Result};
use flate2::{read::GzEncoder, Compression};
use image::ImageFormat;
use std::{
cell::RefCell,
io::{BufRead, Cursor, Read, Seek},
path::Path,
pin::Pin,
};
use tokio::io;
pub trait Store {
fn store(&self, path: &Path) -> Result<Pin<Box<dyn io::AsyncBufRead + Send>>>;
}
impl Store for String {
fn store(&self, _path: &Path) -> Result<Pin<Box<dyn io::AsyncBufRead + Send>>> {
Ok(Box::pin(Cursor::new(self.clone())))
}
}
impl Store for image::DynamicImage {
fn store(&self, path: &Path) -> Result<Pin<Box<dyn io::AsyncBufRead + Send>>> {
let mut cursor = match ImageFormat::from_path(path).with_context(|| {
format!(
"Can't determine an image format from path: {}",
path.display()
)
})? {
#[cfg(feature = "turbojpeg")]
ImageFormat::Jpeg => {
use image::GenericImageView;
use turbojpeg::{Compressor, Image, PixelFormat, Subsamp};
let (format, subsampling) = match &self {
Self::ImageLuma8(_) => (PixelFormat::GRAY, Subsamp::Gray),
Self::ImageRgb8(_) => (PixelFormat::RGB, Subsamp::None),
Self::ImageRgba8(_) => (PixelFormat::RGBA, Subsamp::None),
c => {
anyhow::bail!(
"Invalid pixel DynamicImage pixel format for Jpeg codec: {c:?}",
)
}
};
let (width, height) = self.dimensions();
let image = Image {
pixels: self.as_bytes(),
width: width as usize,
pitch: format.size() * width as usize,
height: height as usize,
format,
};
let mut compressor = Compressor::new()?;
compressor.set_quality(75)?;
compressor.set_subsamp(subsampling)?;
let v = compressor.compress_to_owned(image.as_deref())?;
Cursor::new(v.to_vec())
}
fmt => {
let v = Vec::new();
let mut cursor = Cursor::new(v);
self.write_to(&mut cursor, fmt)
.with_context(|| format!("Can't encode image for path: {}", path.display()))?;
cursor
}
};
cursor.rewind()?;
Ok(Box::pin(cursor))
}
}
pub struct GzStore<R: BufRead>(RefCell<GzEncoder<R>>);
impl<R: BufRead> GzStore<R> {
pub fn new(r: R) -> Self {
GzStore(RefCell::new(GzEncoder::new(r, Compression::fast())))
}
}
impl<R: BufRead> Store for GzStore<R> {
fn store(&self, _path: &Path) -> Result<Pin<Box<dyn io::AsyncBufRead + Send>>> {
let mut buff = Vec::new();
self.0.borrow_mut().read_to_end(&mut buff)?;
Ok(Box::pin(Cursor::new(buff)))
}
}