use crate::core::error::XmpResult;
use crate::files::handler::{FileHandler, XmpOptions};
use std::io::{Read, Seek, Write};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Handler {
#[cfg(feature = "gif")]
Gif(crate::files::formats::gif::GifHandler),
#[cfg(feature = "jpeg")]
Jpeg(crate::files::formats::jpeg::JpegHandler),
#[cfg(feature = "mpeg4")]
Mpeg4(crate::files::formats::bmff::Mpeg4Handler),
#[cfg(feature = "mpegh")]
Mpegh(crate::files::formats::bmff::MpeghHandler),
#[cfg(feature = "mp3")]
Mp3(crate::files::formats::mp3::Mp3Handler),
#[cfg(feature = "pdf")]
Pdf(crate::files::formats::pdf::PdfHandler),
#[cfg(feature = "png")]
Png(crate::files::formats::png::PngHandler),
#[cfg(feature = "psd")]
Psd(crate::files::formats::psd::PsdHandler),
#[cfg(feature = "svg")]
Svg(crate::files::formats::svg::SvgHandler),
#[cfg(feature = "tiff")]
Tiff(crate::files::formats::tiff::TiffHandler),
#[cfg(feature = "avi")]
Avi(crate::files::formats::riff::avi::AviHandler),
#[cfg(feature = "wav")]
Wav(crate::files::formats::riff::wav::WavHandler),
#[cfg(feature = "webp")]
Webp(crate::files::formats::riff::webp::WebpHandler),
}
impl FileHandler for Handler {
fn can_handle<R: Read + Seek>(&self, reader: &mut R) -> XmpResult<bool> {
match self {
#[cfg(feature = "avi")]
Handler::Avi(h) => h.can_handle(reader),
#[cfg(feature = "gif")]
Handler::Gif(h) => h.can_handle(reader),
#[cfg(feature = "jpeg")]
Handler::Jpeg(h) => h.can_handle(reader),
#[cfg(feature = "mp3")]
Handler::Mp3(h) => h.can_handle(reader),
#[cfg(feature = "mpeg4")]
Handler::Mpeg4(h) => h.can_handle(reader),
#[cfg(feature = "mpegh")]
Handler::Mpegh(h) => h.can_handle(reader),
#[cfg(feature = "pdf")]
Handler::Pdf(h) => h.can_handle(reader),
#[cfg(feature = "png")]
Handler::Png(h) => h.can_handle(reader),
#[cfg(feature = "psd")]
Handler::Psd(h) => h.can_handle(reader),
#[cfg(feature = "svg")]
Handler::Svg(h) => h.can_handle(reader),
#[cfg(feature = "tiff")]
Handler::Tiff(h) => h.can_handle(reader),
#[cfg(feature = "wav")]
Handler::Wav(h) => h.can_handle(reader),
#[cfg(feature = "webp")]
Handler::Webp(h) => h.can_handle(reader),
}
}
fn read_xmp<R: Read + Seek>(
&self,
reader: &mut R,
options: &XmpOptions,
) -> XmpResult<Option<crate::core::metadata::XmpMeta>> {
match self {
#[cfg(feature = "avi")]
Handler::Avi(h) => h.read_xmp(reader, options),
#[cfg(feature = "gif")]
Handler::Gif(h) => h.read_xmp(reader, options),
#[cfg(feature = "jpeg")]
Handler::Jpeg(h) => h.read_xmp(reader, options),
#[cfg(feature = "mp3")]
Handler::Mp3(h) => h.read_xmp(reader, options),
#[cfg(feature = "mpeg4")]
Handler::Mpeg4(h) => h.read_xmp(reader, options),
#[cfg(feature = "mpegh")]
Handler::Mpegh(h) => h.read_xmp(reader, options),
#[cfg(feature = "pdf")]
Handler::Pdf(h) => h.read_xmp(reader, options),
#[cfg(feature = "png")]
Handler::Png(h) => h.read_xmp(reader, options),
#[cfg(feature = "psd")]
Handler::Psd(h) => h.read_xmp(reader, options),
#[cfg(feature = "svg")]
Handler::Svg(h) => h.read_xmp(reader, options),
#[cfg(feature = "tiff")]
Handler::Tiff(h) => h.read_xmp(reader, options),
#[cfg(feature = "wav")]
Handler::Wav(h) => h.read_xmp(reader, options),
#[cfg(feature = "webp")]
Handler::Webp(h) => h.read_xmp(reader, options),
}
}
fn write_xmp<R: Read + Seek, W: Seek + Write>(
&self,
reader: &mut R,
writer: &mut W,
meta: &crate::core::metadata::XmpMeta,
) -> XmpResult<()> {
match self {
#[cfg(feature = "avi")]
Handler::Avi(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "gif")]
Handler::Gif(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "jpeg")]
Handler::Jpeg(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "mp3")]
Handler::Mp3(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "mpeg4")]
Handler::Mpeg4(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "mpegh")]
Handler::Mpegh(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "pdf")]
Handler::Pdf(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "png")]
Handler::Png(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "psd")]
Handler::Psd(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "svg")]
Handler::Svg(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "tiff")]
Handler::Tiff(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "wav")]
Handler::Wav(h) => h.write_xmp(reader, writer, meta),
#[cfg(feature = "webp")]
Handler::Webp(h) => h.write_xmp(reader, writer, meta),
}
}
fn format_name(&self) -> &'static str {
match self {
#[cfg(feature = "avi")]
Handler::Avi(h) => h.format_name(),
#[cfg(feature = "gif")]
Handler::Gif(h) => h.format_name(),
#[cfg(feature = "jpeg")]
Handler::Jpeg(h) => h.format_name(),
#[cfg(feature = "mp3")]
Handler::Mp3(h) => h.format_name(),
#[cfg(feature = "mpeg4")]
Handler::Mpeg4(h) => h.format_name(),
#[cfg(feature = "mpegh")]
Handler::Mpegh(h) => h.format_name(),
#[cfg(feature = "pdf")]
Handler::Pdf(h) => h.format_name(),
#[cfg(feature = "png")]
Handler::Png(h) => h.format_name(),
#[cfg(feature = "psd")]
Handler::Psd(h) => h.format_name(),
#[cfg(feature = "svg")]
Handler::Svg(h) => h.format_name(),
#[cfg(feature = "tiff")]
Handler::Tiff(h) => h.format_name(),
#[cfg(feature = "wav")]
Handler::Wav(h) => h.format_name(),
#[cfg(feature = "webp")]
Handler::Webp(h) => h.format_name(),
}
}
fn extensions(&self) -> &'static [&'static str] {
match self {
#[cfg(feature = "avi")]
Handler::Avi(h) => h.extensions(),
#[cfg(feature = "gif")]
Handler::Gif(h) => h.extensions(),
#[cfg(feature = "jpeg")]
Handler::Jpeg(h) => h.extensions(),
#[cfg(feature = "mp3")]
Handler::Mp3(h) => h.extensions(),
#[cfg(feature = "mpeg4")]
Handler::Mpeg4(h) => h.extensions(),
#[cfg(feature = "mpegh")]
Handler::Mpegh(h) => h.extensions(),
#[cfg(feature = "pdf")]
Handler::Pdf(h) => h.extensions(),
#[cfg(feature = "png")]
Handler::Png(h) => h.extensions(),
#[cfg(feature = "psd")]
Handler::Psd(h) => h.extensions(),
#[cfg(feature = "svg")]
Handler::Svg(h) => h.extensions(),
#[cfg(feature = "tiff")]
Handler::Tiff(h) => h.extensions(),
#[cfg(feature = "wav")]
Handler::Wav(h) => h.extensions(),
#[cfg(feature = "webp")]
Handler::Webp(h) => h.extensions(),
}
}
}
pub struct HandlerRegistry {
handlers: Vec<Handler>,
}
impl HandlerRegistry {
pub fn new() -> Self {
let mut registry = Self {
handlers: Vec::new(),
};
registry.register_defaults();
registry
}
pub fn register(&mut self, handler: Handler) {
self.handlers.push(handler);
}
fn register_defaults(&mut self) {
#[cfg(feature = "avi")]
self.register(Handler::Avi(crate::files::formats::riff::avi::AviHandler));
#[cfg(feature = "gif")]
self.register(Handler::Gif(crate::files::formats::gif::GifHandler));
#[cfg(feature = "jpeg")]
self.register(Handler::Jpeg(crate::files::formats::jpeg::JpegHandler));
#[cfg(feature = "mp3")]
self.register(Handler::Mp3(crate::files::formats::mp3::Mp3Handler));
#[cfg(feature = "mpeg4")]
self.register(Handler::Mpeg4(crate::files::formats::bmff::Mpeg4Handler));
#[cfg(feature = "mpegh")]
self.register(Handler::Mpegh(crate::files::formats::bmff::MpeghHandler));
#[cfg(feature = "pdf")]
self.register(Handler::Pdf(crate::files::formats::pdf::PdfHandler));
#[cfg(feature = "png")]
self.register(Handler::Png(crate::files::formats::png::PngHandler));
#[cfg(feature = "psd")]
self.register(Handler::Psd(crate::files::formats::psd::PsdHandler));
#[cfg(feature = "svg")]
self.register(Handler::Svg(crate::files::formats::svg::SvgHandler));
#[cfg(feature = "tiff")]
self.register(Handler::Tiff(crate::files::formats::tiff::TiffHandler));
#[cfg(feature = "wav")]
self.register(Handler::Wav(crate::files::formats::riff::wav::WavHandler));
#[cfg(feature = "webp")]
self.register(Handler::Webp(
crate::files::formats::riff::webp::WebpHandler,
));
}
pub fn find_by_extension(&self, extension: &str) -> Option<&Handler> {
let ext_lower = extension.to_lowercase();
self.handlers
.iter()
.find(|h| h.extensions().iter().any(|e| e.to_lowercase() == ext_lower))
}
pub fn find_by_detection<R: Read + Seek>(&self, reader: &mut R) -> XmpResult<Option<&Handler>> {
let saved_pos = reader.stream_position()?;
for handler in &self.handlers {
reader.seek(std::io::SeekFrom::Start(saved_pos))?;
if handler.can_handle(reader)? {
reader.seek(std::io::SeekFrom::Start(saved_pos))?;
return Ok(Some(handler));
}
}
reader.seek(std::io::SeekFrom::Start(saved_pos))?;
Ok(None)
}
pub fn handlers(&self) -> &[Handler] {
&self.handlers
}
}
impl Default for HandlerRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn default_registry() -> HandlerRegistry {
HandlerRegistry::new()
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_registry_new() {
let registry = HandlerRegistry::new();
assert!(!registry.handlers().is_empty());
}
#[test]
fn test_find_by_extension() {
let registry = HandlerRegistry::new();
#[cfg(feature = "gif")]
assert!(registry.find_by_extension("gif").is_some());
#[cfg(feature = "jpeg")]
{
assert!(registry.find_by_extension("jpg").is_some());
assert!(registry.find_by_extension("jpeg").is_some());
}
#[cfg(feature = "mp3")]
assert!(registry.find_by_extension("mp3").is_some());
#[cfg(feature = "mpeg4")]
{
assert!(registry.find_by_extension("mp4").is_some());
assert!(registry.find_by_extension("m4a").is_some());
assert!(registry.find_by_extension("m4v").is_some());
}
#[cfg(feature = "mov")]
assert!(registry.find_by_extension("mov").is_some());
#[cfg(feature = "pdf")]
assert!(registry.find_by_extension("pdf").is_some());
#[cfg(feature = "png")]
assert!(registry.find_by_extension("png").is_some());
#[cfg(feature = "tiff")]
{
assert!(registry.find_by_extension("tif").is_some());
assert!(registry.find_by_extension("tiff").is_some());
}
#[cfg(feature = "webp")]
assert!(registry.find_by_extension("webp").is_some());
#[cfg(feature = "psd")]
{
assert!(registry.find_by_extension("psd").is_some());
assert!(registry.find_by_extension("psb").is_some());
}
#[cfg(feature = "svg")]
assert!(registry.find_by_extension("svg").is_some());
assert!(registry.find_by_extension("unknown").is_none());
assert!(registry.find_by_extension("xyz").is_none());
}
#[cfg(feature = "gif")]
#[test]
fn test_find_by_detection_gif() {
let registry = HandlerRegistry::new();
let gif_data = vec![0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x00, 0x00];
let mut reader = Cursor::new(gif_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "GIF");
}
#[cfg(feature = "jpeg")]
#[test]
fn test_find_by_detection_jpeg() {
let registry = HandlerRegistry::new();
let jpeg_data = vec![0xFF, 0xD8, 0xFF, 0xE0];
let mut reader = Cursor::new(jpeg_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "JPEG");
}
#[cfg(feature = "mp3")]
#[test]
fn test_find_by_detection_mp3() {
let registry = HandlerRegistry::new();
let mp3_data = vec![0x49, 0x44, 0x33, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let mut reader = Cursor::new(mp3_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "MP3");
}
#[cfg(feature = "mpeg4")]
#[test]
fn test_find_by_detection_mp4() {
let registry = HandlerRegistry::new();
let mp4_data = vec![
0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D, 0x00, 0x00, 0x00, 0x00, 0x69, 0x73, 0x6F, 0x6D, 0x61, 0x76, 0x63, 0x31, ];
let mut reader = Cursor::new(mp4_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "MP4");
}
#[cfg(feature = "pdf")]
#[test]
fn test_find_by_detection_pdf() {
let registry = HandlerRegistry::new();
let pdf_data = b"%PDF-1.4\n%\xe2\xe3\xcf\xd3\n".to_vec();
let mut reader = Cursor::new(pdf_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "PDF");
}
#[cfg(feature = "png")]
#[test]
fn test_find_by_detection_png() {
let registry = HandlerRegistry::new();
let png_data = vec![0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
let mut reader = Cursor::new(png_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "PNG");
}
#[cfg(feature = "tiff")]
#[test]
fn test_find_by_detection_tiff_le() {
let registry = HandlerRegistry::new();
let mut tiff_data = vec![
0x49, 0x49, 0x2A, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, ];
tiff_data.resize(26, 0);
let mut reader = Cursor::new(tiff_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "TIFF");
}
#[cfg(feature = "tiff")]
#[test]
fn test_find_by_detection_tiff_be() {
let registry = HandlerRegistry::new();
let mut tiff_data = vec![
0x4D, 0x4D, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x08, 0x00, 0x01, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, ];
tiff_data.resize(26, 0);
let mut reader = Cursor::new(tiff_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "TIFF");
}
#[cfg(feature = "webp")]
#[test]
fn test_find_by_detection_webp() {
let registry = HandlerRegistry::new();
let webp_data = vec![
0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x45, 0x42, 0x50, ];
let mut reader = Cursor::new(webp_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "WebP");
}
#[cfg(feature = "psd")]
#[test]
fn test_find_by_detection_psd() {
let registry = HandlerRegistry::new();
let mut psd_data = vec![
0x38, 0x42, 0x50, 0x53, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x64, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ];
psd_data.resize(40, 0); let mut reader = Cursor::new(psd_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "PSD");
}
#[cfg(feature = "svg")]
#[test]
fn test_find_by_detection_svg() {
let registry = HandlerRegistry::new();
let svg_data = br#"<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100">
</svg>"#;
let mut reader = Cursor::new(svg_data.to_vec());
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_some());
assert_eq!(handler.unwrap().format_name(), "SVG");
}
#[test]
fn test_find_by_detection_unknown() {
let registry = HandlerRegistry::new();
let unknown_data = vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let mut reader = Cursor::new(unknown_data);
let handler = registry.find_by_detection(&mut reader).unwrap();
assert!(handler.is_none());
}
}