1use byteorder::WriteBytesExt;
6
7#[derive(Copy, Clone, Debug, Eq, PartialEq)]
9pub enum Version {
10 V1,
12
13 V2,
15}
16
17impl std::fmt::Display for Version {
18 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19 write!(f, "{}", u8::from(*self))
20 }
21}
22
23impl From<Version> for u8 {
24 fn from(value: Version) -> Self {
25 match value {
26 Version::V1 => 1,
27 Version::V2 => 2,
28 }
29 }
30}
31
32impl TryFrom<u8> for Version {
33 type Error = ();
34
35 fn try_from(value: u8) -> Result<Self, Self::Error> {
36 match value {
37 1 => Ok(Self::V1),
38 2 => Ok(Self::V2),
39 _ => Err(()),
40 }
41 }
42}
43
44const MAGIC_BYTES: [u8; 3] = [b'F', b'J', b'L'];
45
46impl Version {
47 pub(crate) fn parse_file_header(bytes: &[u8]) -> Option<Self> {
48 let first_three = bytes.get(0..3)?;
49
50 if first_three == MAGIC_BYTES {
51 let version = *bytes.get(3)?;
52 let version = Self::try_from(version).ok()?;
53
54 Some(version)
55 } else {
56 None
57 }
58 }
59
60 pub(crate) fn write_file_header<W: std::io::Write>(
61 self,
62 writer: &mut W,
63 ) -> std::io::Result<()> {
64 writer.write_all(&MAGIC_BYTES)?;
65 writer.write_u8(u8::from(self))?;
66 Ok(())
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use test_log::test;
74
75 #[test]
76 #[allow(clippy::expect_used)]
77 pub fn version_serialize() -> crate::Result<()> {
78 let mut bytes = vec![];
79 Version::V1.write_file_header(&mut bytes)?;
80 assert_eq!(bytes, &[b'F', b'J', b'L', 1]);
81 Ok(())
82 }
83
84 #[test]
85 #[allow(clippy::expect_used)]
86 pub fn version_serialize_2() -> crate::Result<()> {
87 let mut bytes = vec![];
88 Version::V2.write_file_header(&mut bytes)?;
89 assert_eq!(bytes, &[b'F', b'J', b'L', 2]);
90 Ok(())
91 }
92
93 #[test]
94 #[allow(clippy::expect_used)]
95 pub fn version_deserialize_success() {
96 let version = Version::parse_file_header(&[b'F', b'J', b'L', 1]);
97 assert_eq!(version, Some(Version::V1));
98 }
99
100 #[test]
101 #[allow(clippy::expect_used)]
102 pub fn version_deserialize_success_2() {
103 let version = Version::parse_file_header(&[b'F', b'J', b'L', 2]);
104 assert_eq!(version, Some(Version::V2));
105 }
106
107 #[test]
108 #[allow(clippy::expect_used)]
109 pub fn version_deserialize_fail() {
110 let version = Version::parse_file_header(&[b'F', b'J', b'X', 1]);
111 assert!(version.is_none());
112 }
113
114 #[test]
115 #[allow(clippy::expect_used)]
116 pub fn version_serde_round_trip() {
117 let mut buf = vec![];
118 Version::V1.write_file_header(&mut buf).expect("can't fail");
119
120 let version = Version::parse_file_header(&buf);
121 assert_eq!(version, Some(Version::V1));
122 }
123
124 #[test]
125 #[allow(clippy::expect_used)]
126 pub fn version_len() {
127 let mut buf = vec![];
128 Version::V1.write_file_header(&mut buf).expect("can't fail");
129 assert_eq!(4, buf.len());
130 }
131}