mvutils/
version.rs

1use crate as mvutils;
2use mvutils_proc_macro::Savable;
3use std::cmp::Ordering;
4use std::fmt::{Debug, Display, Formatter};
5use std::hash::{Hash, Hasher};
6use std::str::FromStr;
7
8#[derive(Eq, PartialEq, Copy, Clone, Savable)]
9pub struct Version {
10    variant: u16,
11    major: u16,
12    minor: u16,
13    patch: u16,
14}
15
16impl Version {
17    pub fn new(variant: u16, major: u16, minor: u16, patch: u16) -> Self {
18        Version {
19            variant,
20            major,
21            minor,
22            patch,
23        }
24    }
25
26    pub fn parse_vulkan(version: u32) -> Self {
27        Version {
28            variant: (version >> 29) as u16,
29            major: ((version >> 22) & 0x7f) as u16,
30            minor: ((version >> 12) & 0x3FF) as u16,
31            patch: (version & 0xFFF) as u16,
32        }
33    }
34
35    pub fn parse(version: &str) -> Option<Self> {
36        if version.is_empty() {
37            return None;
38        }
39        let digits = if version.starts_with("#version") {
40            version
41                .replace("#version", "")
42                .replace(' ', "")
43                .chars()
44                .map(|c| c as u16 - 48)
45                .collect::<Vec<_>>()
46        } else {
47            let results = version
48                .replace(['v', ' '], "")
49                .split('.')
50                .map(u16::from_str)
51                .collect::<Vec<_>>();
52            if results.iter().any(Result::is_err) {
53                return None;
54            }
55            results.into_iter().map(|e| e.unwrap()).collect::<Vec<_>>()
56        };
57
58        if digits.len() == 1 {
59            Some(Version {
60                variant: 0,
61                major: digits[0],
62                minor: 0,
63                patch: 0,
64            })
65        } else if digits.len() == 2 {
66            Some(Version {
67                variant: 0,
68                major: digits[0],
69                minor: digits[1],
70                patch: 0,
71            })
72        } else if digits.len() == 3 {
73            Some(Version {
74                variant: 0,
75                major: digits[0],
76                minor: digits[1],
77                patch: digits[2],
78            })
79        } else {
80            None
81        }
82    }
83
84    pub fn to_glsl_string(&self) -> String {
85        format!("#version {}{}{}", self.major, self.minor, self.patch)
86    }
87
88    pub fn as_vulkan_version(&self) -> u32 {
89        ((self.variant as u32) << 29 | (self.major as u32) << 22)
90            | ((self.minor as u32) << 12)
91            | (self.patch as u32)
92    }
93
94    pub fn variant(&self) -> u16 {
95        self.variant
96    }
97
98    pub fn major(&self) -> u16 {
99        self.major
100    }
101
102    pub fn minor(&self) -> u16 {
103        self.minor
104    }
105
106    pub fn patch(&self) -> u16 {
107        self.patch
108    }
109
110    pub fn set_variant(&mut self, variant: u16) {
111        self.variant = variant;
112    }
113
114    pub fn set_major(&mut self, major: u16) {
115        self.major = major;
116    }
117
118    pub fn set_minor(&mut self, minor: u16) {
119        self.minor = minor;
120    }
121
122    pub fn set_patch(&mut self, patch: u16) {
123        self.patch = patch;
124    }
125}
126
127impl Default for Version {
128    fn default() -> Self {
129        Version {
130            variant: 0,
131            major: 1,
132            minor: 0,
133            patch: 0,
134        }
135    }
136}
137
138impl Display for Version {
139    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
140        f.write_str(format!("{}.{}.{}", self.major, self.minor, self.patch).as_str())
141    }
142}
143
144impl Debug for Version {
145    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
146        f.write_str(
147            format!(
148                "Version {{ major: {}, minor: {}, patch: {} }}",
149                self.major, self.minor, self.patch
150            )
151            .as_str(),
152        )
153    }
154}
155
156impl Ord for Version {
157    fn cmp(&self, other: &Self) -> Ordering {
158        self.major
159            .cmp(&other.major)
160            .then(self.minor.cmp(&other.minor))
161            .then(self.patch.cmp(&other.patch))
162    }
163}
164
165impl PartialOrd for Version {
166    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
167        Some(self.cmp(other))
168    }
169}
170
171impl Hash for Version {
172    fn hash<H: Hasher>(&self, state: &mut H) {
173        state.write_u16(self.major);
174        state.write_u16(self.minor);
175        state.write_u16(self.patch);
176    }
177}
178
179impl FromStr for Version {
180    type Err = ();
181
182    fn from_str(s: &str) -> Result<Self, Self::Err> {
183        Version::parse(s).ok_or(())
184    }
185}
186
187impl TryFrom<String> for Version {
188    type Error = ();
189
190    fn try_from(value: String) -> Result<Self, Self::Error> {
191        Version::parse(&value).ok_or(())
192    }
193}
194
195impl From<u32> for Version {
196    fn from(value: u32) -> Self {
197        Version::parse_vulkan(value)
198    }
199}
200
201impl From<Version> for u32 {
202    fn from(value: Version) -> Self {
203        value.as_vulkan_version()
204    }
205}