exif_oxide/formats/
detection.rs1use crate::types::{ExifError, Result};
8use std::fs::File;
9use std::io::{BufReader, Read, Seek, SeekFrom};
10use std::path::Path;
11
12pub fn detect_file_format<R: Read + Seek>(mut reader: R) -> Result<FileFormat> {
17 let mut magic_bytes = [0u8; 4];
18 reader.read_exact(&mut magic_bytes)?;
19
20 reader.seek(SeekFrom::Start(0))?;
22
23 match &magic_bytes[0..2] {
24 [0xFF, 0xD8] => Ok(FileFormat::Jpeg),
26 [0x49, 0x49] | [0x4D, 0x4D] => Ok(FileFormat::Tiff),
28 _ => {
29 Err(ExifError::Unsupported(
31 "Unsupported file format - not a JPEG or TIFF".to_string(),
32 ))
33 }
34 }
35}
36
37pub fn detect_file_format_from_path(path: &Path) -> Result<FileFormat> {
39 let file = File::open(path)?;
40 let reader = BufReader::new(file);
41 detect_file_format(reader)
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum FileFormat {
47 Jpeg,
48 Tiff,
49 CanonRaw,
50 NikonRaw,
51 SonyRaw,
52 Dng,
53}
54
55impl FileFormat {
56 pub fn mime_type(&self) -> &'static str {
58 match self {
59 FileFormat::Jpeg => "image/jpeg",
60 FileFormat::Tiff => "image/tiff",
61 FileFormat::CanonRaw => "image/x-canon-cr2",
62 FileFormat::NikonRaw => "image/x-nikon-nef",
63 FileFormat::SonyRaw => "image/x-sony-arw",
64 FileFormat::Dng => "image/x-adobe-dng",
65 }
66 }
67
68 pub fn extension(&self) -> &'static str {
70 match self {
71 FileFormat::Jpeg => "jpg",
72 FileFormat::Tiff => "tif",
73 FileFormat::CanonRaw => "cr2",
74 FileFormat::NikonRaw => "nef",
75 FileFormat::SonyRaw => "arw",
76 FileFormat::Dng => "dng",
77 }
78 }
79
80 pub fn file_type(&self) -> &'static str {
84 match self {
85 FileFormat::Jpeg => "JPEG",
86 FileFormat::Tiff => "TIFF",
87 FileFormat::CanonRaw => "CR2",
88 FileFormat::NikonRaw => "NEF",
89 FileFormat::SonyRaw => "ARW",
90 FileFormat::Dng => "DNG",
91 }
92 }
93
94 pub fn file_type_extension(&self) -> &'static str {
100 match self {
101 FileFormat::Jpeg => "jpg", FileFormat::Tiff => "tif", FileFormat::CanonRaw => "cr2",
106 FileFormat::NikonRaw => "nef",
107 FileFormat::SonyRaw => "arw",
108 FileFormat::Dng => "dng",
109 }
110 }
111}
112
113pub fn get_format_properties(format: FileFormat) -> FormatProperties {
115 FormatProperties {
116 mime_type: format.mime_type(),
117 extension: format.extension(),
118 supports_exif: matches!(format, FileFormat::Jpeg | FileFormat::Tiff),
119 supports_makernotes: matches!(format, FileFormat::Jpeg | FileFormat::Tiff),
120 }
121}
122
123#[derive(Debug, Clone)]
125pub struct FormatProperties {
126 pub mime_type: &'static str,
127 pub extension: &'static str,
128 pub supports_exif: bool,
129 pub supports_makernotes: bool,
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use std::io::Cursor;
136
137 #[test]
138 fn test_jpeg_magic_bytes() {
139 let jpeg_magic = [0xFF, 0xD8, 0xFF, 0xE0]; let cursor = Cursor::new(jpeg_magic);
141 let format = detect_file_format(cursor).unwrap();
142 assert_eq!(format, FileFormat::Jpeg);
143 }
144
145 #[test]
146 fn test_tiff_magic_bytes_little_endian() {
147 let tiff_magic = [0x49, 0x49, 0x2A, 0x00]; let cursor = Cursor::new(tiff_magic);
149 let format = detect_file_format(cursor).unwrap();
150 assert_eq!(format, FileFormat::Tiff);
151 }
152
153 #[test]
154 fn test_tiff_magic_bytes_big_endian() {
155 let tiff_magic = [0x4D, 0x4D, 0x00, 0x2A]; let cursor = Cursor::new(tiff_magic);
157 let format = detect_file_format(cursor).unwrap();
158 assert_eq!(format, FileFormat::Tiff);
159 }
160
161 #[test]
162 fn test_unsupported_format() {
163 let unknown_magic = [0x12, 0x34, 0x56, 0x78];
164 let cursor = Cursor::new(unknown_magic);
165 let result = detect_file_format(cursor);
166 assert!(result.is_err());
167 }
168
169 #[test]
170 fn test_format_properties() {
171 let jpeg_props = get_format_properties(FileFormat::Jpeg);
172 assert_eq!(jpeg_props.mime_type, "image/jpeg");
173 assert_eq!(jpeg_props.extension, "jpg");
174 assert!(jpeg_props.supports_exif);
175 assert!(jpeg_props.supports_makernotes);
176
177 let tiff_props = get_format_properties(FileFormat::Tiff);
178 assert_eq!(tiff_props.mime_type, "image/tiff");
179 assert_eq!(tiff_props.extension, "tif");
180 assert!(tiff_props.supports_exif);
181 assert!(tiff_props.supports_makernotes);
182 }
183}