use std::fs;
use std::path::Path;
use crate::error::Result;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PreviewFormat {
Png,
Jpeg,
Webp,
Unknown,
}
impl PreviewFormat {
pub(crate) fn detect(bytes: &[u8]) -> Self {
if bytes.starts_with(b"\x89PNG\r\n\x1a\n") {
Self::Png
} else if bytes.starts_with(&[0xFF, 0xD8, 0xFF]) {
Self::Jpeg
} else if bytes.len() >= 12 && &bytes[..4] == b"RIFF" && &bytes[8..12] == b"WEBP" {
Self::Webp
} else {
Self::Unknown
}
}
pub fn extension(self) -> &'static str {
match self {
Self::Png => "png",
Self::Jpeg => "jpg",
Self::Webp => "webp",
Self::Unknown => "bin",
}
}
pub fn media_type(self) -> &'static str {
match self {
Self::Png => "image/png",
Self::Jpeg => "image/jpeg",
Self::Webp => "image/webp",
Self::Unknown => "application/octet-stream",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Preview {
format: PreviewFormat,
bytes: Vec<u8>,
width: Option<u32>,
height: Option<u32>,
}
impl Preview {
pub fn new(format: PreviewFormat, bytes: Vec<u8>) -> Self {
Self {
format,
bytes,
width: None,
height: None,
}
}
pub fn with_dimensions(mut self, width: u32, height: u32) -> Self {
self.width = Some(width);
self.height = Some(height);
self
}
pub fn format(&self) -> PreviewFormat {
self.format
}
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
pub fn len(&self) -> usize {
self.bytes().len()
}
pub fn is_empty(&self) -> bool {
self.bytes().is_empty()
}
pub fn dimensions(&self) -> Option<(u32, u32)> {
Some((self.width?, self.height?))
}
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
fs::write(path, self.bytes())?;
Ok(())
}
}