1 | /// A version number for a specific component of an OpenGL implementation
|
2 | #[derive (Clone, Eq, Ord, PartialEq, PartialOrd)]
|
3 | pub 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 |
|
11 | impl Version {
|
12 | /// Create a new OpenGL version number
|
13 | #[allow (dead_code)]
|
14 | pub(crate) fn new(major: u32, minor: u32, revision: Option<u32>, vendor_info: String) -> Self {
|
15 | Version {
|
16 | major: major,
|
17 | minor: minor,
|
18 | is_embedded: false,
|
19 | revision: revision,
|
20 | vendor_info,
|
21 | }
|
22 | }
|
23 |
|
24 | /// Create a new OpenGL ES version number
|
25 | #[allow (dead_code)]
|
26 | pub(crate) fn new_embedded(major: u32, minor: u32, vendor_info: String) -> Self {
|
27 | Version {
|
28 | major,
|
29 | minor,
|
30 | is_embedded: true,
|
31 | revision: None,
|
32 | vendor_info,
|
33 | }
|
34 | }
|
35 |
|
36 | /// According to the OpenGL specification, the version information is
|
37 | /// expected to follow the following syntax:
|
38 | ///
|
39 | /// ~~~bnf
|
40 | /// <major> ::= <number>
|
41 | /// <minor> ::= <number>
|
42 | /// <revision> ::= <number>
|
43 | /// <vendor-info> ::= <string>
|
44 | /// <release> ::= <major> "." <minor> ["." <release>]
|
45 | /// <version> ::= <release> [" " <vendor-info>]
|
46 | /// ~~~
|
47 | ///
|
48 | /// Note that this function is intentionally lenient in regards to parsing,
|
49 | /// and will try to recover at least the first two version numbers without
|
50 | /// resulting in an `Err`.
|
51 | /// # Notes
|
52 | /// `WebGL 2` version returned as `OpenGL ES 3.0`
|
53 | pub(crate) fn parse(mut src: &str) -> Result<Version, &str> {
|
54 | let webgl_sig = "WebGL " ;
|
55 | // According to the WebGL specification
|
56 | // VERSION WebGL<space>1.0<space><vendor-specific information>
|
57 | // SHADING_LANGUAGE_VERSION WebGL<space>GLSL<space>ES<space>1.0<space><vendor-specific information>
|
58 | let is_webgl = src.starts_with(webgl_sig);
|
59 | let is_es = if is_webgl {
|
60 | let pos = src.rfind(webgl_sig).unwrap_or(0);
|
61 | src = &src[pos + webgl_sig.len()..];
|
62 | true
|
63 | } else {
|
64 | let es_sig = " ES " ;
|
65 | match src.rfind(es_sig) {
|
66 | Some(pos) => {
|
67 | src = &src[pos + es_sig.len()..];
|
68 | true
|
69 | }
|
70 | None => false,
|
71 | }
|
72 | };
|
73 |
|
74 | let glsl_es_sig = "GLSL ES " ;
|
75 | let is_glsl = match src.find(glsl_es_sig) {
|
76 | Some(pos) => {
|
77 | src = &src[pos + glsl_es_sig.len()..];
|
78 | true
|
79 | }
|
80 | None => false,
|
81 | };
|
82 |
|
83 | let (version, vendor_info) = match src.find(' ' ) {
|
84 | Some(i) => (&src[..i], src[i + 1..].to_string()),
|
85 | None => (src, String::new()),
|
86 | };
|
87 |
|
88 | // TODO: make this even more lenient so that we can also accept
|
89 | // `<major> "." <minor> [<???>]`
|
90 | let mut it = version.split('.' );
|
91 | let major = it.next().and_then(|s| s.parse().ok());
|
92 | let minor = it.next().and_then(|s| {
|
93 | let trimmed = if s.starts_with('0' ) {
|
94 | "0"
|
95 | } else {
|
96 | s.trim_end_matches('0' )
|
97 | };
|
98 | trimmed.parse().ok()
|
99 | });
|
100 | let revision = if is_webgl {
|
101 | None
|
102 | } else {
|
103 | it.next().and_then(|s| s.parse().ok())
|
104 | };
|
105 |
|
106 | match (major, minor, revision) {
|
107 | (Some(major), Some(minor), revision) => Ok(Version {
|
108 | // Return WebGL 2.0 version as OpenGL ES 3.0
|
109 | major: if is_webgl && !is_glsl {
|
110 | major + 1
|
111 | } else {
|
112 | major
|
113 | },
|
114 | minor,
|
115 | is_embedded: is_es,
|
116 | revision,
|
117 | vendor_info,
|
118 | }),
|
119 | (_, _, _) => Err(src),
|
120 | }
|
121 | }
|
122 | }
|
123 |
|
124 | impl std::fmt::Debug for Version {
|
125 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
126 | match (
|
127 | self.major,
|
128 | self.minor,
|
129 | self.revision,
|
130 | self.vendor_info.as_str(),
|
131 | ) {
|
132 | (major: u32, minor: u32, Some(revision: u32), "" ) => write!(f, " {}. {}. {}" , major, minor, revision),
|
133 | (major: u32, minor: u32, None, "" ) => write!(f, " {}. {}" , major, minor),
|
134 | (major: u32, minor: u32, Some(revision: u32), vendor_info: &str) => {
|
135 | write!(f, " {}. {}. {}, {}" , major, minor, revision, vendor_info)
|
136 | }
|
137 | (major: u32, minor: u32, None, vendor_info: &str) => write!(f, " {}. {}, {}" , major, minor, vendor_info),
|
138 | }
|
139 | }
|
140 | }
|
141 |
|
142 | #[cfg (test)]
|
143 | mod tests {
|
144 | use super::Version;
|
145 |
|
146 | #[test ]
|
147 | fn test_version_parse() {
|
148 | assert_eq!(Version::parse("1" ), Err("1" ));
|
149 | assert_eq!(Version::parse("1." ), Err("1." ));
|
150 | assert_eq!(Version::parse("1 h3l1o. W0rld" ), Err("1 h3l1o. W0rld" ));
|
151 | assert_eq!(Version::parse("1. h3l1o. W0rld" ), Err("1. h3l1o. W0rld" ));
|
152 | assert_eq!(
|
153 | Version::parse("1.2.3" ),
|
154 | Ok(Version::new(1, 2, Some(3), String::new()))
|
155 | );
|
156 | assert_eq!(
|
157 | Version::parse("1.2" ),
|
158 | Ok(Version::new(1, 2, None, String::new()))
|
159 | );
|
160 | assert_eq!(
|
161 | Version::parse("1.2 h3l1o. W0rld" ),
|
162 | Ok(Version::new(1, 2, None, "h3l1o. W0rld" .to_string()))
|
163 | );
|
164 | assert_eq!(
|
165 | Version::parse("1.2.h3l1o. W0rld" ),
|
166 | Ok(Version::new(1, 2, None, "W0rld" .to_string()))
|
167 | );
|
168 | assert_eq!(
|
169 | Version::parse("1.2. h3l1o. W0rld" ),
|
170 | Ok(Version::new(1, 2, None, "h3l1o. W0rld" .to_string()))
|
171 | );
|
172 | assert_eq!(
|
173 | Version::parse("1.2.3.h3l1o. W0rld" ),
|
174 | Ok(Version::new(1, 2, Some(3), "W0rld" .to_string()))
|
175 | );
|
176 | assert_eq!(
|
177 | Version::parse("1.2.3 h3l1o. W0rld" ),
|
178 | Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld" .to_string()))
|
179 | );
|
180 | assert_eq!(
|
181 | Version::parse("OpenGL ES 3.1" ),
|
182 | Ok(Version::new_embedded(3, 1, String::new()))
|
183 | );
|
184 | assert_eq!(
|
185 | Version::parse("OpenGL ES 2.0 Google Nexus" ),
|
186 | Ok(Version::new_embedded(2, 0, "Google Nexus" .to_string()))
|
187 | );
|
188 | assert_eq!(
|
189 | Version::parse("GLSL ES 1.1" ),
|
190 | Ok(Version::new_embedded(1, 1, String::new()))
|
191 | );
|
192 | assert_eq!(
|
193 | Version::parse("OpenGL ES GLSL ES 3.20" ),
|
194 | Ok(Version::new_embedded(3, 2, String::new()))
|
195 | );
|
196 | assert_eq!(
|
197 | // WebGL 2.0 should parse as OpenGL ES 3.0
|
198 | Version::parse("WebGL 2.0 (OpenGL ES 3.0 Chromium)" ),
|
199 | Ok(Version::new_embedded(
|
200 | 3,
|
201 | 0,
|
202 | "(OpenGL ES 3.0 Chromium)" .to_string()
|
203 | ))
|
204 | );
|
205 | assert_eq!(
|
206 | Version::parse("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)" ),
|
207 | Ok(Version::new_embedded(
|
208 | 3,
|
209 | 0,
|
210 | "(OpenGL ES GLSL ES 3.0 Chromium)" .to_string()
|
211 | ))
|
212 | );
|
213 | }
|
214 | }
|
215 | |