Skip to main content

datacard_rs/
format.rs

1//! Card format trait - implementors define their card structure
2//!
3//! Each card format (Ternsig, CML, etc.) implements this trait to define:
4//! - Magic bytes for identification
5//! - Version constraints
6//! - Payload validation
7
8use crate::error::Result;
9
10/// Trait for defining card formats
11///
12/// Implementors define their magic bytes, version requirements, and validation logic.
13/// The datacard library handles generic I/O, checksums, and structure.
14///
15/// # Example
16///
17/// ```ignore
18/// use datacard_rs::{CardFormat, CardError};
19///
20/// pub struct TernsigFormat;
21///
22/// impl CardFormat for TernsigFormat {
23///     const MAGIC: [u8; 4] = *b"TERN";
24///     const VERSION_MAJOR: u8 = 1;
25///     const VERSION_MINOR: u8 = 0;
26///
27///     fn validate_payload(payload: &[u8]) -> Result<(), CardError> {
28///         // Ternsig-specific validation
29///         Ok(())
30///     }
31/// }
32/// ```
33pub trait CardFormat: Sized {
34    /// 4-byte magic identifier (e.g., b"TERN", b"CARD")
35    const MAGIC: [u8; 4];
36
37    /// Major version number
38    const VERSION_MAJOR: u8;
39
40    /// Minimum supported minor version
41    const VERSION_MINOR: u8;
42
43    /// Validate magic bytes match this format
44    fn validate_magic(magic: &[u8; 4]) -> Result<()> {
45        if magic != &Self::MAGIC {
46            return Err(crate::CardError::InvalidMagic(*magic));
47        }
48        Ok(())
49    }
50
51    /// Validate version is compatible
52    fn validate_version(major: u8, minor: u8) -> Result<()> {
53        if major != Self::VERSION_MAJOR {
54            return Err(crate::CardError::UnsupportedVersion { major, minor });
55        }
56        // Minor version must be >= our minimum
57        if minor < Self::VERSION_MINOR {
58            return Err(crate::CardError::UnsupportedVersion { major, minor });
59        }
60        Ok(())
61    }
62
63    /// Validate payload after reading (optional, default no-op)
64    fn validate_payload(_payload: &[u8]) -> Result<()> {
65        Ok(())
66    }
67
68    /// Format name for error messages
69    fn format_name() -> &'static str;
70}