1#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
3pub struct Version {
4 pub major: u32,
5 pub minor: u32,
6 pub is_embedded: bool,
7 pub revision: Option<u32>,
8 pub vendor_info: String,
9}
10
11impl Version {
12 pub(crate) fn new(major: u32, minor: u32, revision: Option<u32>, vendor_info: String) -> Self {
14 Version {
15 major: major,
16 minor: minor,
17 is_embedded: false,
18 revision: revision,
19 vendor_info,
20 }
21 }
22 pub(crate) fn new_embedded(major: u32, minor: u32, vendor_info: String) -> Self {
24 Version {
25 major,
26 minor,
27 is_embedded: true,
28 revision: None,
29 vendor_info,
30 }
31 }
32
33 pub(crate) fn parse(mut src: &str) -> Result<Version, &str> {
51 let webgl_sig = "WebGL ";
52 let is_webgl = src.starts_with(webgl_sig);
56 let is_es = if is_webgl {
57 let pos = src.rfind(webgl_sig).unwrap_or(0);
58 src = &src[pos + webgl_sig.len()..];
59 true
60 } else {
61 let es_sig = " ES ";
62 match src.rfind(es_sig) {
63 Some(pos) => {
64 src = &src[pos + es_sig.len()..];
65 true
66 }
67 None => false,
68 }
69 };
70
71 let glsl_es_sig = "GLSL ES ";
72 let is_glsl = match src.find(glsl_es_sig) {
73 Some(pos) => {
74 src = &src[pos + glsl_es_sig.len()..];
75 true
76 }
77 None => false,
78 };
79
80 let (version, vendor_info) = match src.find(' ') {
81 Some(i) => (&src[..i], src[i + 1..].to_string()),
82 None => (src, String::new()),
83 };
84
85 let mut it = version.split('.');
88 let major = it.next().and_then(|s| s.parse().ok());
89 let minor = it.next().and_then(|s| {
90 let trimmed = if s.starts_with('0') {
91 "0"
92 } else {
93 s.trim_end_matches('0')
94 };
95 trimmed.parse().ok()
96 });
97 let revision = if is_webgl {
98 None
99 } else {
100 it.next().and_then(|s| s.parse().ok())
101 };
102
103 match (major, minor, revision) {
104 (Some(major), Some(minor), revision) => Ok(Version {
105 major: if is_webgl && !is_glsl {
107 major + 1
108 } else {
109 major
110 },
111 minor,
112 is_embedded: is_es,
113 revision,
114 vendor_info,
115 }),
116 (_, _, _) => Err(src),
117 }
118 }
119}
120
121impl std::fmt::Debug for Version {
122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
123 match (
124 self.major,
125 self.minor,
126 self.revision,
127 self.vendor_info.as_str(),
128 ) {
129 (major, minor, Some(revision), "") => write!(f, "{}.{}.{}", major, minor, revision),
130 (major, minor, None, "") => write!(f, "{}.{}", major, minor),
131 (major, minor, Some(revision), vendor_info) => {
132 write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info)
133 }
134 (major, minor, None, vendor_info) => write!(f, "{}.{}, {}", major, minor, vendor_info),
135 }
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::Version;
142
143 #[test]
144 fn test_version_parse() {
145 assert_eq!(Version::parse("1"), Err("1"));
146 assert_eq!(Version::parse("1."), Err("1."));
147 assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
148 assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
149 assert_eq!(
150 Version::parse("1.2.3"),
151 Ok(Version::new(1, 2, Some(3), String::new()))
152 );
153 assert_eq!(
154 Version::parse("1.2"),
155 Ok(Version::new(1, 2, None, String::new()))
156 );
157 assert_eq!(
158 Version::parse("1.2 h3l1o. W0rld"),
159 Ok(Version::new(1, 2, None, "h3l1o. W0rld".to_string()))
160 );
161 assert_eq!(
162 Version::parse("1.2.h3l1o. W0rld"),
163 Ok(Version::new(1, 2, None, "W0rld".to_string()))
164 );
165 assert_eq!(
166 Version::parse("1.2. h3l1o. W0rld"),
167 Ok(Version::new(1, 2, None, "h3l1o. W0rld".to_string()))
168 );
169 assert_eq!(
170 Version::parse("1.2.3.h3l1o. W0rld"),
171 Ok(Version::new(1, 2, Some(3), "W0rld".to_string()))
172 );
173 assert_eq!(
174 Version::parse("1.2.3 h3l1o. W0rld"),
175 Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld".to_string()))
176 );
177 assert_eq!(
178 Version::parse("OpenGL ES 3.1"),
179 Ok(Version::new_embedded(3, 1, String::new()))
180 );
181 assert_eq!(
182 Version::parse("OpenGL ES 2.0 Google Nexus"),
183 Ok(Version::new_embedded(2, 0, "Google Nexus".to_string()))
184 );
185 assert_eq!(
186 Version::parse("GLSL ES 1.1"),
187 Ok(Version::new_embedded(1, 1, String::new()))
188 );
189 assert_eq!(
190 Version::parse("OpenGL ES GLSL ES 3.20"),
191 Ok(Version::new_embedded(3, 2, String::new()))
192 );
193 assert_eq!(
194 Version::parse("WebGL 2.0 (OpenGL ES 3.0 Chromium)"),
196 Ok(Version::new_embedded(
197 3,
198 0,
199 "(OpenGL ES 3.0 Chromium)".to_string()
200 ))
201 );
202 assert_eq!(
203 Version::parse("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)"),
204 Ok(Version::new_embedded(
205 3,
206 0,
207 "(OpenGL ES GLSL ES 3.0 Chromium)".to_string()
208 ))
209 );
210 }
211}