datacard-rs 1.0.0

Generic binary card format library with checksums and pluggable format traits
Documentation
//! Card format trait - implementors define their card structure
//!
//! Each card format (Ternsig, CML, etc.) implements this trait to define:
//! - Magic bytes for identification
//! - Version constraints
//! - Payload validation

use crate::error::Result;

/// Trait for defining card formats
///
/// Implementors define their magic bytes, version requirements, and validation logic.
/// The datacard library handles generic I/O, checksums, and structure.
///
/// # Example
///
/// ```ignore
/// use datacard_rs::{CardFormat, CardError};
///
/// pub struct TernsigFormat;
///
/// impl CardFormat for TernsigFormat {
///     const MAGIC: [u8; 4] = *b"TERN";
///     const VERSION_MAJOR: u8 = 1;
///     const VERSION_MINOR: u8 = 0;
///
///     fn validate_payload(payload: &[u8]) -> Result<(), CardError> {
///         // Ternsig-specific validation
///         Ok(())
///     }
/// }
/// ```
pub trait CardFormat: Sized {
    /// 4-byte magic identifier (e.g., b"TERN", b"CARD")
    const MAGIC: [u8; 4];

    /// Major version number
    const VERSION_MAJOR: u8;

    /// Minimum supported minor version
    const VERSION_MINOR: u8;

    /// Validate magic bytes match this format
    fn validate_magic(magic: &[u8; 4]) -> Result<()> {
        if magic != &Self::MAGIC {
            return Err(crate::CardError::InvalidMagic(*magic));
        }
        Ok(())
    }

    /// Validate version is compatible
    fn validate_version(major: u8, minor: u8) -> Result<()> {
        if major != Self::VERSION_MAJOR {
            return Err(crate::CardError::UnsupportedVersion { major, minor });
        }
        // Minor version must be >= our minimum
        if minor < Self::VERSION_MINOR {
            return Err(crate::CardError::UnsupportedVersion { major, minor });
        }
        Ok(())
    }

    /// Validate payload after reading (optional, default no-op)
    fn validate_payload(_payload: &[u8]) -> Result<()> {
        Ok(())
    }

    /// Format name for error messages
    fn format_name() -> &'static str;
}