ahri_tre_protocol/
version.rs1use semver::Version;
2use serde::{Deserialize, Serialize};
3use std::fmt;
4
5pub const CURRENT_PROTOCOL_VERSION: &str = "2.0.0";
6pub const PROTOCOL_COMPATIBILITY_RULE: &str = "same_major_not_newer";
7
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub struct ProtocolCompatibility {
10 pub current: String,
11 pub compatibility: ProtocolCompatibilityRange,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct ProtocolCompatibilityRange {
16 pub minimum: String,
17 pub maximum: String,
18 pub rule: String,
19}
20
21pub fn current_protocol_compatibility() -> ProtocolCompatibility {
22 let current = ProtocolVersion::current();
23
24 ProtocolCompatibility {
25 current: current.to_string(),
26 compatibility: ProtocolCompatibilityRange {
27 minimum: format!("{}.0.0", current.major()),
28 maximum: current.to_string(),
29 rule: PROTOCOL_COMPATIBILITY_RULE.to_string(),
30 },
31 }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35#[serde(try_from = "String", into = "String")]
36pub struct ProtocolVersion(Version);
37
38impl ProtocolVersion {
39 pub fn current() -> Self {
40 Self(Version::parse(CURRENT_PROTOCOL_VERSION).expect("current protocol version is valid"))
41 }
42
43 pub fn parse(value: impl AsRef<str>) -> Result<Self, ProtocolVersionError> {
44 let value = value.as_ref();
45 if value.starts_with('v') {
46 return Err(ProtocolVersionError::InvalidFormat);
47 }
48 let version = Version::parse(value).map_err(|_| ProtocolVersionError::InvalidFormat)?;
49 if !version.pre.is_empty() || !version.build.is_empty() {
50 return Err(ProtocolVersionError::InvalidFormat);
51 }
52 Ok(Self(version))
53 }
54
55 pub fn ensure_supported(&self, server: &Self) -> Result<(), ProtocolVersionError> {
56 if self.0.major != server.0.major {
57 return Err(ProtocolVersionError::UnsupportedMajor);
58 }
59 if self.0 > server.0 {
60 return Err(ProtocolVersionError::UnsupportedVersion);
61 }
62 Ok(())
63 }
64
65 pub const fn major(&self) -> u64 {
66 self.0.major
67 }
68}
69
70impl Default for ProtocolVersion {
71 fn default() -> Self {
72 Self::current()
73 }
74}
75
76impl fmt::Display for ProtocolVersion {
77 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
78 self.0.fmt(formatter)
79 }
80}
81
82impl TryFrom<String> for ProtocolVersion {
83 type Error = ProtocolVersionError;
84
85 fn try_from(value: String) -> Result<Self, Self::Error> {
86 Self::parse(value)
87 }
88}
89
90impl From<ProtocolVersion> for String {
91 fn from(value: ProtocolVersion) -> Self {
92 value.to_string()
93 }
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum ProtocolVersionError {
98 InvalidFormat,
99 UnsupportedMajor,
100 UnsupportedVersion,
101}
102
103impl fmt::Display for ProtocolVersionError {
104 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 Self::InvalidFormat => formatter.write_str("invalid protocol version format"),
107 Self::UnsupportedMajor => formatter.write_str("unsupported protocol major version"),
108 Self::UnsupportedVersion => formatter.write_str("unsupported protocol version"),
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn compatibility_range_is_owned_by_protocol_version_contract() {
119 let compatibility = current_protocol_compatibility();
120 let current = ProtocolVersion::current();
121
122 assert_eq!(compatibility.current, CURRENT_PROTOCOL_VERSION);
123 assert_eq!(compatibility.compatibility.minimum, "2.0.0");
124 assert_eq!(compatibility.compatibility.maximum, current.to_string());
125 assert_eq!(
126 compatibility.compatibility.rule,
127 PROTOCOL_COMPATIBILITY_RULE
128 );
129 }
130}