1use std::str::FromStr;
2
3use num_enum::TryFromPrimitive;
4
5#[derive(Clone, PartialEq, PartialOrd)]
9pub struct UnityVersion {
10 pub major: u16,
11 pub minor: u16,
12 pub build: u16,
13 pub typ: UnityVersionType,
14 pub build_number: u8,
15 pub trailing_data: String,
16}
17
18impl UnityVersion {
19 pub fn new(major: u16, minor: u16, build: u16) -> Self {
21 UnityVersion {
22 major,
23 minor,
24 build,
25 typ: UnityVersionType::Final,
26 build_number: 1,
27 trailing_data: String::new(),
28 }
29 }
30
31 pub fn version_tuple(&self) -> (u16, u16, u16) {
32 (self.major, self.minor, self.build)
33 }
34}
35
36impl std::fmt::Debug for UnityVersion {
37 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38 std::fmt::Display::fmt(self, f)
39 }
40}
41
42impl std::fmt::Display for UnityVersion {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 write!(
45 f,
46 "{}.{}.{}{}{}",
47 self.major,
48 self.minor,
49 self.build,
50 self.typ.char(),
51 self.build_number,
52 )?;
53 if !self.trailing_data.is_empty() {
54 write!(f, "-{}", self.trailing_data)?;
55 }
56 Ok(())
57 }
58}
59
60#[non_exhaustive]
61#[derive(Debug)]
62pub struct UnityVersionParseError(String);
63
64impl std::fmt::Display for UnityVersionParseError {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "Could not parse unity version {}", self.0)
67 }
68}
69
70impl std::error::Error for UnityVersionParseError {}
71
72impl FromStr for UnityVersion {
73 type Err = UnityVersionParseError;
74
75 fn from_str(s: &str) -> Result<Self, Self::Err> {
76 (|| {
78 let mut split = s.split('.');
79 let major = split.next()?.parse().ok()?;
80 let minor = split.next()?.parse().ok()?;
81 let rest = split.next()?;
82
83 let i = rest.find(char::is_alphabetic)?;
84 let build = &rest[..i];
85 let typ = match rest.as_bytes()[i] as char {
86 'a' => UnityVersionType::Alpha,
87 'b' => UnityVersionType::Beta,
88 'p' => UnityVersionType::Patch,
90 'f' => UnityVersionType::Final,
91 _ => return None,
93 };
94 let rest = &rest[i + 1..];
95
96 let (build_number, trailing_data) = rest.split_once('-').unwrap_or((rest, ""));
97
98 Some(UnityVersion {
99 major,
100 minor,
101 build: build.parse().ok()?,
102 typ,
103 build_number: build_number.parse().ok()?,
104 trailing_data: trailing_data.to_owned(),
105 })
106 })()
107 .ok_or_else(|| UnityVersionParseError(s.to_owned()))
108 }
109}
110
111#[repr(u8)]
112#[derive(Copy, Clone, Debug, TryFromPrimitive, PartialEq, PartialOrd)]
113pub enum UnityVersionType {
114 Alpha = 0,
115 Beta = 1,
116 China = 2,
117 Final = 3,
118 Patch = 4,
119 Experimental = 5,
120}
121
122impl UnityVersionType {
123 pub fn char(self) -> char {
124 match self {
125 UnityVersionType::Alpha => 'a',
126 UnityVersionType::Beta => 'b',
127 UnityVersionType::China => 'c', UnityVersionType::Final => 'f',
129 UnityVersionType::Patch => 'p', UnityVersionType::Experimental => 'e', }
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::{UnityVersion, UnityVersionType};
138
139 #[test]
140 fn unity_version_simple() {
141 let version: UnityVersion = "2022.2.2a13".parse().unwrap();
142
143 assert_eq!(
144 version,
145 UnityVersion {
146 major: 2022,
147 minor: 2,
148 build: 2,
149 typ: UnityVersionType::Alpha,
150 build_number: 13,
151 trailing_data: "".into(),
152 }
153 )
154 }
155
156 #[test]
157 fn unity_version_complex() {
158 let version: UnityVersion = "6000.0.50f1-uum-100966-branch1".parse().unwrap();
159
160 assert_eq!(
161 version,
162 UnityVersion {
163 major: 6000,
164 minor: 0,
165 build: 50,
166 typ: UnityVersionType::Final,
167 build_number: 1,
168 trailing_data: "uum-100966-branch1".into()
169 }
170 )
171 }
172}