1use crate::error::Result;
4use crate::CardError;
5use std::io::{Read, Write};
6
7pub const MAGIC: &[u8; 4] = b"CARD";
9
10pub const VERSION_MAJOR: u8 = 1;
12
13pub const VERSION_MINOR: u8 = 0;
15
16pub const FLAG_HAS_CHECKSUM: u16 = 0x01;
18
19pub const FLAG_HAS_TIMESTAMP: u16 = 0x02;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct CardHeader {
25 pub magic: [u8; 4],
26 pub major: u8,
27 pub minor: u8,
28 pub flags: u16,
29}
30
31impl CardHeader {
32 pub fn new() -> Self {
34 Self {
35 magic: *MAGIC,
36 major: VERSION_MAJOR,
37 minor: VERSION_MINOR,
38 flags: 0,
39 }
40 }
41
42 pub fn with_flags(flags: u16) -> Self {
44 Self {
45 magic: *MAGIC,
46 major: VERSION_MAJOR,
47 minor: VERSION_MINOR,
48 flags,
49 }
50 }
51
52 pub fn has_checksum(&self) -> bool {
54 self.flags & FLAG_HAS_CHECKSUM != 0
55 }
56
57 pub fn has_timestamp(&self) -> bool {
59 self.flags & FLAG_HAS_TIMESTAMP != 0
60 }
61
62 pub fn write_to<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
64 writer.write_all(&self.magic)?;
65 writer.write_all(&[self.major])?;
66 writer.write_all(&[self.minor])?;
67 writer.write_all(&self.flags.to_le_bytes())?;
68 Ok(())
69 }
70
71 pub fn to_bytes(&self) -> Vec<u8> {
73 let mut bytes = Vec::with_capacity(8);
74 bytes.extend_from_slice(&self.magic);
75 bytes.push(self.major);
76 bytes.push(self.minor);
77 bytes.extend_from_slice(&self.flags.to_le_bytes());
78 bytes
79 }
80
81 pub fn read_from<R: Read>(reader: &mut R) -> Result<Self> {
83 let mut magic = [0u8; 4];
84 reader.read_exact(&mut magic)?;
85
86 let mut version = [0u8; 2];
87 reader.read_exact(&mut version)?;
88
89 let mut flags_bytes = [0u8; 2];
90 reader.read_exact(&mut flags_bytes)?;
91
92 Ok(Self {
93 magic,
94 major: version[0],
95 minor: version[1],
96 flags: u16::from_le_bytes(flags_bytes),
97 })
98 }
99
100 pub fn validate(&self) -> Result<()> {
102 if &self.magic != MAGIC {
103 return Err(CardError::InvalidMagic(self.magic));
104 }
105
106 if self.major != VERSION_MAJOR {
107 return Err(CardError::UnsupportedVersion {
108 major: self.major,
109 minor: self.minor,
110 });
111 }
112
113 Ok(())
114 }
115}
116
117impl Default for CardHeader {
118 fn default() -> Self {
119 Self::new()
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use std::io::Cursor;
127
128 #[test]
129 fn test_header_new() {
130 let header = CardHeader::new();
131 assert_eq!(header.magic, *MAGIC);
132 assert_eq!(header.major, VERSION_MAJOR);
133 assert_eq!(header.minor, VERSION_MINOR);
134 assert_eq!(header.flags, 0);
135 }
136
137 #[test]
138 fn test_header_with_flags() {
139 let header = CardHeader::with_flags(FLAG_HAS_CHECKSUM | FLAG_HAS_TIMESTAMP);
140 assert!(header.has_checksum());
141 assert!(header.has_timestamp());
142 }
143
144 #[test]
145 fn test_header_roundtrip() {
146 let header = CardHeader::new();
147 let mut buffer = Vec::new();
148 header.write_to(&mut buffer).unwrap();
149
150 let mut cursor = Cursor::new(&buffer);
151 let loaded = CardHeader::read_from(&mut cursor).unwrap();
152
153 assert_eq!(loaded, header);
154 }
155
156 #[test]
157 fn test_header_validation() {
158 let mut header = CardHeader::new();
159 assert!(header.validate().is_ok());
160
161 header.magic = *b"FAKE";
163 assert!(header.validate().is_err());
164
165 header.magic = *MAGIC;
167 header.major = 99;
168 assert!(header.validate().is_err());
169 }
170
171 #[test]
172 fn test_header_to_bytes() {
173 let header = CardHeader::with_flags(FLAG_HAS_CHECKSUM);
174 let bytes = header.to_bytes();
175
176 assert_eq!(bytes.len(), 8);
177 assert_eq!(&bytes[0..4], MAGIC);
178 assert_eq!(bytes[4], VERSION_MAJOR);
179 assert_eq!(bytes[5], VERSION_MINOR);
180 assert_eq!(u16::from_le_bytes([bytes[6], bytes[7]]), FLAG_HAS_CHECKSUM);
181 }
182}