1use serde::{Deserialize, Serialize};
4use std::fmt;
5use thiserror::Error;
6
7#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ProtocolVersion {
14 pub major: u16,
15 pub minor: u16,
16}
17
18impl ProtocolVersion {
19 pub const CURRENT: ProtocolVersion = ProtocolVersion { major: 1, minor: 0 };
21
22 pub const fn new(major: u16, minor: u16) -> Self {
24 Self { major, minor }
25 }
26
27 pub fn parse(s: &str) -> Result<Self, VersionError> {
29 let parts: Vec<&str> = s.split('.').collect();
30 if parts.len() != 2 {
31 return Err(VersionError::InvalidFormat(s.to_string()));
32 }
33
34 let major = parts
35 .first()
36 .ok_or_else(|| VersionError::InvalidFormat(s.to_string()))?
37 .parse::<u16>()
38 .map_err(|_| VersionError::InvalidFormat(s.to_string()))?;
39
40 let minor = parts
41 .get(1)
42 .ok_or_else(|| VersionError::InvalidFormat(s.to_string()))?
43 .parse::<u16>()
44 .map_err(|_| VersionError::InvalidFormat(s.to_string()))?;
45
46 Ok(Self { major, minor })
47 }
48
49 pub fn is_compatible_with(&self, other: &ProtocolVersion) -> bool {
55 self.major == other.major
56 }
57
58 pub fn is_newer_than(&self, other: &ProtocolVersion) -> bool {
60 (self.major, self.minor) > (other.major, other.minor)
61 }
62
63 pub fn is_current(&self) -> bool {
65 *self == Self::CURRENT
66 }
67}
68
69impl Default for ProtocolVersion {
70 fn default() -> Self {
71 Self::CURRENT
72 }
73}
74
75impl fmt::Display for ProtocolVersion {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 write!(f, "{}.{}", self.major, self.minor)
78 }
79}
80
81#[derive(Error, Debug, Clone)]
83pub enum VersionError {
84 #[error("Invalid version format: {0}")]
85 InvalidFormat(String),
86
87 #[error("Incompatible version: got {got}, expected {expected}")]
88 Incompatible { got: String, expected: String },
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_version_parse() {
97 let v = ProtocolVersion::parse("1.0").unwrap();
98 assert_eq!(v.major, 1);
99 assert_eq!(v.minor, 0);
100 }
101
102 #[test]
103 fn test_version_parse_invalid() {
104 assert!(ProtocolVersion::parse("1").is_err());
105 assert!(ProtocolVersion::parse("1.0.0").is_err());
106 assert!(ProtocolVersion::parse("abc").is_err());
107 }
108
109 #[test]
110 fn test_version_compatibility() {
111 let v1_0 = ProtocolVersion::new(1, 0);
112 let v1_1 = ProtocolVersion::new(1, 1);
113 let v2_0 = ProtocolVersion::new(2, 0);
114
115 assert!(v1_0.is_compatible_with(&v1_1));
116 assert!(v1_1.is_compatible_with(&v1_0));
117 assert!(!v1_0.is_compatible_with(&v2_0));
118 }
119
120 #[test]
121 fn test_version_display() {
122 let v = ProtocolVersion::new(1, 2);
123 assert_eq!(format!("{v}"), "1.2");
124 }
125}