1use crate::{ByteCursor, Error};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct Header {
6 pub format_version: u32,
7 pub writer_version: RVersion,
8 pub minimum_reader_version: RVersion,
9 pub native_encoding: Option<String>,
10}
11
12impl Header {
13 pub fn parse(cursor: &mut ByteCursor<'_>) -> Result<Self, Error> {
14 let marker_offset = cursor.position();
15 let marker = cursor.read_exact(2)?;
16 if marker != b"X\n" {
17 return Err(Error::UnsupportedMarker {
18 marker: [marker[0], marker[1]],
19 offset: marker_offset,
20 });
21 }
22
23 let version_offset = cursor.position();
24 let format_version = cursor.read_be_u32()?;
25 if format_version != 2 && format_version != 3 {
26 return Err(Error::UnsupportedVersion {
27 version: format_version,
28 offset: version_offset,
29 });
30 }
31
32 let writer_version = RVersion::from_raw(cursor.read_be_u32()?);
33 let minimum_reader_version = RVersion::from_raw(cursor.read_be_u32()?);
34
35 let native_encoding = if format_version == 3 {
36 let byte_len = cursor.read_be_u32()? as usize;
37 let encoding_offset = cursor.position();
38 let encoding = cursor.read_exact(byte_len)?;
39 Some(
40 String::from_utf8(encoding.to_vec()).map_err(|_| Error::InvalidUtf8 {
41 offset: encoding_offset,
42 })?,
43 )
44 } else {
45 None
46 };
47
48 Ok(Self {
49 format_version,
50 writer_version,
51 minimum_reader_version,
52 native_encoding,
53 })
54 }
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub struct RVersion {
60 raw: u32,
61}
62
63impl RVersion {
64 pub fn from_raw(raw: u32) -> Self {
65 Self { raw }
66 }
67
68 pub fn raw(self) -> u32 {
69 self.raw
70 }
71
72 pub fn components(self) -> (u32, u32, u32) {
73 (self.raw >> 16, (self.raw >> 8) & 0xff, self.raw & 0xff)
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn truncated_input_returns_offset() {
83 let mut cursor = ByteCursor::new(b"X\n");
84 let err = Header::parse(&mut cursor).unwrap_err();
85 assert!(matches!(
86 err,
87 Error::UnexpectedEof {
88 offset: 2,
89 needed: 4,
90 remaining: 0
91 }
92 ));
93 }
94
95 #[test]
96 fn wrong_marker_is_reported() {
97 let mut cursor = ByteCursor::new(b"A\n\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00");
98 let err = Header::parse(&mut cursor).unwrap_err();
99 assert!(matches!(
100 err,
101 Error::UnsupportedMarker {
102 marker: [b'A', b'\n'],
103 offset: 0
104 }
105 ));
106 }
107
108 #[test]
109 fn unsupported_version_is_reported() {
110 let mut cursor = ByteCursor::new(b"X\n\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00");
111 let err = Header::parse(&mut cursor).unwrap_err();
112 assert!(matches!(
113 err,
114 Error::UnsupportedVersion {
115 version: 4,
116 offset: 2
117 }
118 ));
119 }
120}