amadeus_utils/
version.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// Version struct that represents a semantic version as three bytes [major, minor, patch]
5#[derive(
6    Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, bincode::Encode, bincode::Decode,
7)]
8pub struct Ver([u8; 3]);
9
10impl Ver {
11    /// Create a new version from three individual version components
12    pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
13        Self([major, minor, patch])
14    }
15
16    /// Create a version from a byte array
17    pub const fn from_bytes(bytes: [u8; 3]) -> Self {
18        Self(bytes)
19    }
20
21    /// Get the raw byte array representation
22    pub const fn as_bytes(&self) -> [u8; 3] {
23        self.0
24    }
25
26    /// Get the major version component
27    pub const fn major(&self) -> u8 {
28        self.0[0]
29    }
30
31    /// Get the minor version component
32    pub const fn minor(&self) -> u8 {
33        self.0[1]
34    }
35
36    /// Get the patch version component
37    pub const fn patch(&self) -> u8 {
38        self.0[2]
39    }
40}
41
42impl fmt::Display for Ver {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        write!(f, "{}.{}.{}", self.0[0], self.0[1], self.0[2])
45    }
46}
47
48impl From<[u8; 3]> for Ver {
49    fn from(bytes: [u8; 3]) -> Self {
50        Self(bytes)
51    }
52}
53
54impl From<Ver> for [u8; 3] {
55    fn from(ver: Ver) -> Self {
56        ver.0
57    }
58}
59
60impl From<Ver> for String {
61    fn from(ver: Ver) -> Self {
62        ver.to_string()
63    }
64}
65
66impl TryFrom<&str> for Ver {
67    type Error = ParseVersionError;
68
69    fn try_from(s: &str) -> Result<Self, Self::Error> {
70        let parts: Vec<&str> = s.split('.').collect();
71        if parts.len() != 3 {
72            return Err(ParseVersionError::InvalidFormat);
73        }
74
75        let major = parts[0].parse::<u8>().map_err(|_| ParseVersionError::InvalidNumber)?;
76        let minor = parts[1].parse::<u8>().map_err(|_| ParseVersionError::InvalidNumber)?;
77        let patch = parts[2].parse::<u8>().map_err(|_| ParseVersionError::InvalidNumber)?;
78
79        Ok(Self::new(major, minor, patch))
80    }
81}
82
83impl TryFrom<String> for Ver {
84    type Error = ParseVersionError;
85
86    fn try_from(s: String) -> Result<Self, Self::Error> {
87        Ver::try_from(s.as_str())
88    }
89}
90
91#[derive(Debug, thiserror::Error)]
92pub enum ParseVersionError {
93    #[error("Invalid version format, expected 'major.minor.patch'")]
94    InvalidFormat,
95    #[error("Invalid version number")]
96    InvalidNumber,
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_ver_creation() {
105        let ver = Ver::new(1, 2, 3);
106        assert_eq!(ver.major(), 1);
107        assert_eq!(ver.minor(), 2);
108        assert_eq!(ver.patch(), 3);
109    }
110
111    #[test]
112    fn test_ver_display() {
113        let ver = Ver::new(1, 1, 8);
114        assert_eq!(ver.to_string(), "1.1.8");
115    }
116
117    #[test]
118    fn test_ver_from_bytes() {
119        let bytes = [1, 1, 7];
120        let ver = Ver::from_bytes(bytes);
121        assert_eq!(ver.as_bytes(), bytes);
122    }
123
124    #[test]
125    fn test_ver_parse_from_string() {
126        let ver = Ver::try_from("1.1.8").unwrap();
127        assert_eq!(ver.major(), 1);
128        assert_eq!(ver.minor(), 1);
129        assert_eq!(ver.patch(), 8);
130    }
131
132    #[test]
133    fn test_ver_parse_invalid_format() {
134        assert!(Ver::try_from("1.1").is_err());
135        assert!(Ver::try_from("1.1.2.3").is_err());
136        assert!(Ver::try_from("a.b.c").is_err());
137    }
138
139    #[test]
140    fn test_ver_ordering() {
141        let v1 = Ver::new(1, 1, 7);
142        let v2 = Ver::new(1, 1, 8);
143        let v3 = Ver::new(1, 2, 0);
144        let v4 = Ver::new(2, 0, 0);
145
146        assert!(v1 < v2);
147        assert!(v2 < v3);
148        assert!(v3 < v4);
149    }
150
151    #[test]
152    fn test_ver_conversions() {
153        let ver = Ver::new(1, 1, 8);
154
155        // Test conversion to bytes
156        let bytes: [u8; 3] = ver.into();
157        assert_eq!(bytes, [1, 1, 8]);
158
159        // Test conversion to string
160        let string: String = ver.into();
161        assert_eq!(string, "1.1.8");
162
163        // Test conversion from bytes
164        let ver2 = Ver::from([1, 1, 8]);
165        assert_eq!(ver, ver2);
166    }
167}