shadow_crypt_core/
version.rs1use crate::errors::HeaderError;
2
3pub const MAGIC: [u8; 6] = *b"SHADOW";
5
6pub const PREAMBLE_LENGTH: usize = MAGIC.len() + 1;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum Version {
11 V1,
12 V2,
13}
14
15impl Version {
16 pub fn as_str(&self) -> &'static str {
17 match self {
18 Version::V1 => "v1",
19 Version::V2 => "v2",
20 }
21 }
22
23 pub fn as_u8(&self) -> u8 {
24 match self {
25 Version::V1 => 1,
26 Version::V2 => 2,
27 }
28 }
29}
30
31impl TryFrom<u8> for Version {
32 type Error = ();
33
34 fn try_from(value: u8) -> Result<Self, Self::Error> {
35 match value {
36 1 => Ok(Version::V1),
37 2 => Ok(Version::V2),
38 _ => Err(()),
39 }
40 }
41}
42
43pub fn read_file_version(bytes: &[u8]) -> Result<Version, HeaderError> {
49 if bytes.len() < PREAMBLE_LENGTH {
50 return Err(HeaderError::InsufficientBytes);
51 }
52 if bytes[..MAGIC.len()] != MAGIC {
53 return Err(HeaderError::InvalidData);
54 }
55 Version::try_from(bytes[MAGIC.len()]).map_err(|_| HeaderError::InvalidData)
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn test_version_as_str() {
64 assert_eq!(Version::V1.as_str(), "v1");
65 assert_eq!(Version::V2.as_str(), "v2");
66 }
67
68 #[test]
69 fn test_version_as_u8() {
70 assert_eq!(Version::V1.as_u8(), 1);
71 assert_eq!(Version::V2.as_u8(), 2);
72 }
73
74 #[test]
75 fn test_try_from_u8() {
76 assert_eq!(Version::try_from(1), Ok(Version::V1));
77 assert_eq!(Version::try_from(2), Ok(Version::V2));
78 assert!(Version::try_from(0).is_err());
79 assert!(Version::try_from(3).is_err());
80 }
81
82 #[test]
83 fn test_read_file_version() {
84 let mut bytes = b"SHADOW".to_vec();
85 bytes.push(2);
86 assert_eq!(read_file_version(&bytes).unwrap(), Version::V2);
87
88 bytes[6] = 1;
89 assert_eq!(read_file_version(&bytes).unwrap(), Version::V1);
90
91 bytes[6] = 99;
92 assert!(read_file_version(&bytes).is_err());
93
94 assert!(read_file_version(b"SHADO").is_err());
95 assert!(read_file_version(b"NOTSHD\x01").is_err());
96 }
97}