1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::str::FromStr;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
19pub struct AwpVersion {
20 pub major: u32,
21 pub minor: u32,
22}
23
24pub const CURRENT_VERSION: AwpVersion = AwpVersion { major: 1, minor: 0 };
26
27impl AwpVersion {
28 pub fn is_compatible(&self, other: &AwpVersion) -> bool {
32 self.major == other.major
33 }
34}
35
36impl fmt::Display for AwpVersion {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 write!(f, "{}.{}", self.major, self.minor)
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct ParseVersionError(pub String);
45
46impl fmt::Display for ParseVersionError {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 write!(f, "invalid AWP version: {}", self.0)
49 }
50}
51
52impl std::error::Error for ParseVersionError {}
53
54impl FromStr for AwpVersion {
55 type Err = ParseVersionError;
56
57 fn from_str(s: &str) -> Result<Self, Self::Err> {
58 let parts: Vec<&str> = s.split('.').collect();
59 if parts.len() != 2 {
60 return Err(ParseVersionError(format!("expected format 'major.minor', got '{s}'")));
61 }
62 let major = parts[0]
63 .parse::<u32>()
64 .map_err(|e| ParseVersionError(format!("invalid major version: {e}")))?;
65 let minor = parts[1]
66 .parse::<u32>()
67 .map_err(|e| ParseVersionError(format!("invalid minor version: {e}")))?;
68 Ok(AwpVersion { major, minor })
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_current_version() {
78 assert_eq!(CURRENT_VERSION.major, 1);
79 assert_eq!(CURRENT_VERSION.minor, 0);
80 }
81
82 #[test]
83 fn test_display() {
84 let v = AwpVersion { major: 1, minor: 0 };
85 assert_eq!(v.to_string(), "1.0");
86
87 let v2 = AwpVersion { major: 2, minor: 3 };
88 assert_eq!(v2.to_string(), "2.3");
89 }
90
91 #[test]
92 fn test_from_str_valid() {
93 let v: AwpVersion = "1.0".parse().unwrap();
94 assert_eq!(v, AwpVersion { major: 1, minor: 0 });
95
96 let v2: AwpVersion = "2.3".parse().unwrap();
97 assert_eq!(v2, AwpVersion { major: 2, minor: 3 });
98 }
99
100 #[test]
101 fn test_from_str_round_trip() {
102 let v = AwpVersion { major: 1, minor: 0 };
103 let s = v.to_string();
104 let parsed: AwpVersion = s.parse().unwrap();
105 assert_eq!(v, parsed);
106 }
107
108 #[test]
109 fn test_from_str_invalid() {
110 assert!("abc".parse::<AwpVersion>().is_err());
111 assert!("1".parse::<AwpVersion>().is_err());
112 assert!("1.2.3".parse::<AwpVersion>().is_err());
113 assert!("a.b".parse::<AwpVersion>().is_err());
114 }
115
116 #[test]
117 fn test_is_compatible_same_major() {
118 let a = AwpVersion { major: 1, minor: 0 };
119 let b = AwpVersion { major: 1, minor: 5 };
120 assert!(a.is_compatible(&b));
121 }
122
123 #[test]
124 fn test_is_compatible_different_major() {
125 let a = AwpVersion { major: 1, minor: 0 };
126 let b = AwpVersion { major: 2, minor: 0 };
127 assert!(!a.is_compatible(&b));
128 }
129}