use std::io::Cursor;
use image::ImageFormat;
use super::{Asset, Context, MediaType, ProcessesAssets, ProcessingError};
pub struct FaviconProcessor;
impl ProcessesAssets for FaviconProcessor {
fn process(&self, _context: &mut Context, asset: &mut Asset) -> Result<(), ProcessingError> {
if asset.media_type() != &MediaType::Png {
return Ok(());
}
let path = asset.path();
let file_name = path.as_str().rsplit('/').next().unwrap_or(path.as_str());
if file_name != "favicon.png" {
return Ok(());
}
tracing::trace!("favicon: {}", asset.path());
let image_bytes = asset.as_bytes();
let png =
image::load_from_memory_with_format(image_bytes, ImageFormat::Png).map_err(|e| {
ProcessingError::Malformed {
message: e.to_string().into(),
}
})?;
let png = png.thumbnail(32, 32);
let ico_frame = image::codecs::ico::IcoFrame::as_png(
png.as_bytes(),
png.width(),
png.height(),
png.color().into(),
)
.map_err(|e| ProcessingError::Malformed {
message: e.to_string().into(),
})?;
let mut ico_bytes = Vec::new();
let ico_encoder = image::codecs::ico::IcoEncoder::new(Cursor::new(&mut ico_bytes));
ico_encoder
.encode_images(&[ico_frame])
.map_err(|e| ProcessingError::Malformed {
message: e.to_string().into(),
})?;
asset.replace_with_bytes(ico_bytes, MediaType::Ico);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn converts_favicon_png_to_ico() {
let source_bytes = std::fs::read("test/example.png").unwrap();
let mut asset = Asset::new("favicon.png".into(), source_bytes);
assert_eq!(asset.media_type(), &MediaType::Png);
FaviconProcessor
.process(&mut Context::default(), &mut asset)
.unwrap();
assert_eq!(asset.media_type(), &MediaType::Ico);
let ico_bytes = asset.as_bytes();
assert!(ico_bytes.len() > 6);
assert_eq!(&ico_bytes[0..4], &[0x00, 0x00, 0x01, 0x00]);
}
#[test]
fn skips_non_favicon_png() {
let source_bytes = std::fs::read("test/example.png").unwrap();
let mut asset = Asset::new("other-image.png".into(), source_bytes.clone());
let original_len = asset.as_bytes().len();
FaviconProcessor
.process(&mut Context::default(), &mut asset)
.unwrap();
assert_eq!(asset.media_type(), &MediaType::Png);
assert_eq!(asset.as_bytes().len(), original_len);
}
}