1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[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 pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
13 Self([major, minor, patch])
14 }
15
16 pub const fn from_bytes(bytes: [u8; 3]) -> Self {
18 Self(bytes)
19 }
20
21 pub const fn as_bytes(&self) -> [u8; 3] {
23 self.0
24 }
25
26 pub const fn major(&self) -> u8 {
28 self.0[0]
29 }
30
31 pub const fn minor(&self) -> u8 {
33 self.0[1]
34 }
35
36 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 let bytes: [u8; 3] = ver.into();
157 assert_eq!(bytes, [1, 1, 8]);
158
159 let string: String = ver.into();
161 assert_eq!(string, "1.1.8");
162
163 let ver2 = Ver::from([1, 1, 8]);
165 assert_eq!(ver, ver2);
166 }
167}