Skip to content

Parser Shared Infrastructure Guide

This document describes the shared infrastructure available for MakerNote parsers in src/parsers/tiff/makernotes/shared/.

Overview

The shared infrastructure provides reusable components that eliminate code duplication across 55+ manufacturer-specific parsers:

  • Generic Decoders: Pre-built value decoders (ON_OFF, YES_NO, QUALITY, etc.)
  • Decoder Macros: Declarative macros for creating custom decoders
  • IFD Parser Base: Shared IFD entry parsing logic
  • Array Extractors: Generic array extraction functions
  • Array Schemas: Declarative array index specifications (NEW)
  • Tag Registry: Centralized tag definition system (ENHANCED)
  • Lens Databases: Unified lens lookup infrastructure (NEW)

Array Schemas

Problem

Many parsers extract CameraSettings arrays with repetitive code:

rust
// Before: Repetitive and error-prone
if let Some(settings) = extract_i16_array(entry, data, byte_order) {
    if settings.len() > 1 {
        tags.insert("Canon:MacroMode".to_string(), decode_macro_mode(settings[1]));
    }
    if settings.len() > 2 {
        tags.insert("Canon:Quality".to_string(), decode_quality(settings[2]));
    }
    // ... 50-200 more indices
}

Solution

Define array schemas declaratively:

rust
use super::shared::{ArraySchema, ArrayIndexDef, generic_decoders::*};

const_decoder!(MACRO_MODE, i16, [(1, "Macro"), (2, "Normal")]);
const_decoder!(QUALITY, i16, [(1, "Economy"), (2, "Normal"), (3, "Fine")]);

static CAMERA_SETTINGS: ArraySchema = ArraySchema {
    name: "CameraSettings",
    indices: &[
        ArrayIndexDef::with_i16_decoder(1, "MacroMode", &MACRO_MODE),
        ArrayIndexDef::with_i16_decoder(2, "Quality", &QUALITY),
        ArrayIndexDef::raw(3, "ISO"),
    ],
};

// Usage in parser:
if let Some(settings) = extract_i16_array(entry, data, byte_order) {
    CAMERA_SETTINGS.process_i16_array(&settings, "Canon", tags);
}

Benefits

  • Declarative: Schema definition is data, not code
  • Compact: 3 lines replaces 50+ lines of if-statements
  • Type-safe: Compiler ensures decoder compatibility
  • Maintainable: Adding indices is trivial

TagRegistry with Array Support

Enhanced API

TagRegistry now supports array-based tags:

rust
let registry = TagRegistry::new()
    .register_simple(0x0001, "Make", &GENERIC_STRING)
    .register_array_schema(0x0002, &CAMERA_SETTINGS);

// Decode simple tag
registry.decode_and_insert(entry, data, byte_order, "Canon", tags);

// Decode array tag
if let Some(array) = extract_i16_array(entry, data, byte_order) {
    registry.decode_array_i16(entry.tag, &array, "Canon", tags);
}

Lens Databases

Problem

10+ parsers have duplicate lens lookup implementations:

rust
// Before: Each parser reimplements this
fn lookup_lens(&self, lens_id: u16) -> Option<String> {
    match lens_id {
        1 => Some("Canon EF 50mm f/1.8".to_string()),
        2 => Some("Canon EF 85mm f/1.4".to_string()),
        // ... 500+ more lenses
        _ => None,
    }
}

Solution

Use shared LensDatabase infrastructure:

rust
use super::shared::{LensDatabase, StaticLensDb};

static CANON_LENSES: [(u16, &str); 500] = [
    (1, "Canon EF 50mm f/1.8"),
    (2, "Canon EF 85mm f/1.4"),
    // ... all lenses
];

static CANON_LENS_DB: StaticLensDb = StaticLensDb::new(&CANON_LENSES);

impl MakerNoteParser for CanonParser {
    fn lookup_lens(&self, lens_id: u16) -> Option<String> {
        CANON_LENS_DB.lookup(lens_id).map(|s| s.to_string())
    }
}

Database Types

StaticLensDb: For exact ID matches (most manufacturers)

rust
static DB: StaticLensDb = StaticLensDb::new(&[
    (1, "Lens A"),
    (2, "Lens B"),
]);

RangeLensDb: For ID ranges

rust
static DB: RangeLensDb = RangeLensDb::new(&[
    (100, 105, "Lens Family A"),  // IDs 100-105
    (200, 210, "Lens Family B"),  // IDs 200-210
]);

CombinedLensDb: For both

rust
static COMBINED: CombinedLensDb = CombinedLensDb::new(
    Some(&STATIC_DB),
    Some(&RANGE_DB),
);

Migration Checklist

When migrating a parser to use shared infrastructure:

  • [ ] Replace decoder functions with const_decoder! macros
  • [ ] Create ArraySchema for CameraSettings-style arrays
  • [ ] Use TagRegistry for tag definitions
  • [ ] Move lens database to LensDatabase implementation
  • [ ] Remove duplicate byte-reading code (use byte_utils)
  • [ ] Use IfdParserBase for IFD parsing
  • [ ] Add tests verifying functionality unchanged

Examples

See src/parsers/tiff/makernotes/shared/tests/ for complete working examples of each pattern.

Future Enhancements

  • Auto-generated documentation from TagRegistry definitions
  • Compile-time tag ID collision detection
  • Perfect hashing for O(1) tag lookups
  • Cross-domain pattern extraction (PNG, PDF, etc.)

Released under the GPL-3.0 License.