exif_oxide/raw/
processor.rs

1//! RAW processing orchestration and handler traits
2//!
3//! This module implements the central RAW processor that dispatches to
4//! manufacturer-specific handlers based on detected format.
5
6use crate::exif::ExifReader;
7use crate::file_detection::FileTypeDetectionResult;
8use crate::types::{ExifError, Result};
9use std::collections::HashMap;
10
11use super::{detector::detect_raw_format, RawFormat};
12
13/// Trait for manufacturer-specific RAW format handlers
14/// Each RAW format (Kyocera, Canon, Nikon, etc.) implements this trait
15/// ExifTool: Each manufacturer module has ProcessBinaryData or custom processing
16pub trait RawFormatHandler: Send + Sync {
17    /// Process RAW format data and extract metadata
18    /// ExifTool: Usually ProcessBinaryData but can be custom for complex formats
19    fn process_raw(&self, reader: &mut ExifReader, data: &[u8]) -> Result<()>;
20
21    /// Get handler name for debugging and logging
22    fn name(&self) -> &'static str;
23
24    /// Validate that this data is the correct format for this handler
25    /// ExifTool: Each module has format validation logic
26    fn validate_format(&self, data: &[u8]) -> bool;
27}
28
29/// Central RAW processor that routes to manufacturer-specific handlers
30/// ExifTool: Main ExifTool dispatcher routes to manufacturer modules
31pub struct RawProcessor {
32    /// Registered format handlers
33    handlers: HashMap<RawFormat, Box<dyn RawFormatHandler>>,
34}
35
36impl RawProcessor {
37    /// Create new RAW processor with all supported handlers registered
38    pub fn new() -> Self {
39        let mut handlers: HashMap<RawFormat, Box<dyn RawFormatHandler>> = HashMap::new();
40
41        // Register Kyocera handler
42        // ExifTool: KyoceraRaw.pm module registration
43        handlers.insert(
44            RawFormat::Kyocera,
45            Box::new(super::formats::kyocera::KyoceraRawHandler::new()),
46        );
47
48        // Register Minolta handler
49        // ExifTool: MinoltaRaw.pm module registration
50        handlers.insert(
51            RawFormat::Minolta,
52            Box::new(super::formats::minolta::MinoltaRawHandler::new()),
53        );
54
55        // Register Panasonic handler
56        // ExifTool: PanasonicRaw.pm module registration
57        handlers.insert(
58            RawFormat::Panasonic,
59            Box::new(super::formats::panasonic::PanasonicRawHandler::new()),
60        );
61
62        // Future handlers will be registered here:
63        // handlers.insert(RawFormat::Canon, Box::new(CanonRawHandler::new()));
64        // handlers.insert(RawFormat::Nikon, Box::new(NikonRawHandler::new()));
65        // handlers.insert(RawFormat::Sony, Box::new(SonyRawHandler::new()));
66
67        Self { handlers }
68    }
69
70    /// Process RAW file data
71    /// ExifTool: Main entry point that detects format and dispatches to appropriate module
72    pub fn process_raw(
73        &self,
74        reader: &mut ExifReader,
75        data: &[u8],
76        detection_result: &FileTypeDetectionResult,
77    ) -> Result<()> {
78        // Detect RAW format
79        let format = detect_raw_format(detection_result);
80
81        // Get the appropriate handler
82        if let Some(handler) = self.handlers.get(&format) {
83            // Validate format before processing
84            if !handler.validate_format(data) {
85                return Err(ExifError::ParseError(format!(
86                    "Invalid {} RAW format - failed validation",
87                    format.name()
88                )));
89            }
90
91            // Process the RAW data
92            handler.process_raw(reader, data)?;
93        } else {
94            return Err(ExifError::Unsupported(format!(
95                "Unsupported RAW format: {}",
96                format.name()
97            )));
98        }
99
100        Ok(())
101    }
102
103    /// Get list of supported formats
104    pub fn supported_formats(&self) -> Vec<RawFormat> {
105        self.handlers.keys().copied().collect()
106    }
107}
108
109impl Default for RawProcessor {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118    use crate::types::TagValue;
119
120    // Mock handler for testing
121    #[allow(dead_code)]
122    struct MockRawHandler {
123        name: String,
124        should_validate: bool,
125    }
126
127    impl MockRawHandler {
128        #[allow(dead_code)]
129        fn new(name: String, should_validate: bool) -> Self {
130            Self {
131                name,
132                should_validate,
133            }
134        }
135    }
136
137    impl RawFormatHandler for MockRawHandler {
138        fn process_raw(&self, reader: &mut ExifReader, _data: &[u8]) -> Result<()> {
139            // Add a test tag to verify processing was called
140            reader.add_test_tag(
141                0x100,
142                TagValue::String("test".to_string()),
143                "TestRAW",
144                "TestIFD",
145            );
146            Ok(())
147        }
148
149        fn name(&self) -> &'static str {
150            // This is a bit of a hack for testing - in real code, name should be static
151            "MockHandler"
152        }
153
154        fn validate_format(&self, _data: &[u8]) -> bool {
155            self.should_validate
156        }
157    }
158
159    #[test]
160    fn test_raw_processor_creation() {
161        let processor = RawProcessor::new();
162        let supported = processor.supported_formats();
163
164        assert!(supported.contains(&RawFormat::Kyocera));
165        assert!(supported.contains(&RawFormat::Minolta));
166        assert!(supported.contains(&RawFormat::Panasonic));
167        assert_eq!(supported.len(), 3); // Should have exactly 3 supported formats
168    }
169
170    #[test]
171    fn test_raw_processor_unsupported_format() {
172        let processor = RawProcessor::new();
173        let mut reader = ExifReader::new();
174
175        let detection_result = FileTypeDetectionResult {
176            file_type: "UNKNOWN".to_string(),
177            format: "UNKNOWN".to_string(),
178            mime_type: "application/octet-stream".to_string(),
179            description: "Unknown format".to_string(),
180        };
181
182        let data = vec![0u8; 100];
183        let result = processor.process_raw(&mut reader, &data, &detection_result);
184
185        assert!(result.is_err());
186        assert!(result
187            .unwrap_err()
188            .to_string()
189            .contains("Unsupported RAW format"));
190    }
191}