#[derive(Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct Version {
pub major: u32,
pub minor: u32,
pub is_embedded: bool,
pub revision: Option<u32>,
pub vendor_info: String,
}
impl Version {
#[allow(dead_code)]
pub(crate) fn new(major: u32, minor: u32, revision: Option<u32>, vendor_info: String) -> Self {
Version {
major: major,
minor: minor,
is_embedded: false,
revision: revision,
vendor_info,
}
}
#[allow(dead_code)]
pub(crate) fn new_embedded(major: u32, minor: u32, vendor_info: String) -> Self {
Version {
major,
minor,
is_embedded: true,
revision: None,
vendor_info,
}
}
pub(crate) fn parse(mut src: &str) -> Result<Version, &str> {
let webgl_sig = "WebGL ";
let is_webgl = src.starts_with(webgl_sig);
let is_es = if is_webgl {
let pos = src.rfind(webgl_sig).unwrap_or(0);
src = &src[pos + webgl_sig.len()..];
true
} else {
let es_sig = " ES ";
match src.rfind(es_sig) {
Some(pos) => {
src = &src[pos + es_sig.len()..];
true
}
None => false,
}
};
let glsl_es_sig = "GLSL ES ";
let is_glsl = match src.find(glsl_es_sig) {
Some(pos) => {
src = &src[pos + glsl_es_sig.len()..];
true
}
None => false,
};
let (version, vendor_info) = match src.find(' ') {
Some(i) => (&src[..i], src[i + 1..].to_string()),
None => (src, String::new()),
};
let mut it = version.split('.');
let major = it.next().and_then(|s| s.parse().ok());
let minor = it.next().and_then(|s| {
let trimmed = if s.starts_with('0') {
"0"
} else {
s.trim_end_matches('0')
};
trimmed.parse().ok()
});
let revision = if is_webgl {
None
} else {
it.next().and_then(|s| s.parse().ok())
};
match (major, minor, revision) {
(Some(major), Some(minor), revision) => Ok(Version {
major: if is_webgl && !is_glsl {
major + 1
} else {
major
},
minor,
is_embedded: is_es,
revision,
vendor_info,
}),
(_, _, _) => Err(src),
}
}
}
impl std::fmt::Debug for Version {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match (
self.major,
self.minor,
self.revision,
self.vendor_info.as_str(),
) {
(major, minor, Some(revision), "") => write!(f, "{}.{}.{}", major, minor, revision),
(major, minor, None, "") => write!(f, "{}.{}", major, minor),
(major, minor, Some(revision), vendor_info) => {
write!(f, "{}.{}.{}, {}", major, minor, revision, vendor_info)
}
(major, minor, None, vendor_info) => write!(f, "{}.{}, {}", major, minor, vendor_info),
}
}
}
#[cfg(test)]
mod tests {
use super::Version;
#[test]
fn test_version_parse() {
assert_eq!(Version::parse("1"), Err("1"));
assert_eq!(Version::parse("1."), Err("1."));
assert_eq!(Version::parse("1 h3l1o. W0rld"), Err("1 h3l1o. W0rld"));
assert_eq!(Version::parse("1. h3l1o. W0rld"), Err("1. h3l1o. W0rld"));
assert_eq!(
Version::parse("1.2.3"),
Ok(Version::new(1, 2, Some(3), String::new()))
);
assert_eq!(
Version::parse("1.2"),
Ok(Version::new(1, 2, None, String::new()))
);
assert_eq!(
Version::parse("1.2 h3l1o. W0rld"),
Ok(Version::new(1, 2, None, "h3l1o. W0rld".to_string()))
);
assert_eq!(
Version::parse("1.2.h3l1o. W0rld"),
Ok(Version::new(1, 2, None, "W0rld".to_string()))
);
assert_eq!(
Version::parse("1.2. h3l1o. W0rld"),
Ok(Version::new(1, 2, None, "h3l1o. W0rld".to_string()))
);
assert_eq!(
Version::parse("1.2.3.h3l1o. W0rld"),
Ok(Version::new(1, 2, Some(3), "W0rld".to_string()))
);
assert_eq!(
Version::parse("1.2.3 h3l1o. W0rld"),
Ok(Version::new(1, 2, Some(3), "h3l1o. W0rld".to_string()))
);
assert_eq!(
Version::parse("OpenGL ES 3.1"),
Ok(Version::new_embedded(3, 1, String::new()))
);
assert_eq!(
Version::parse("OpenGL ES 2.0 Google Nexus"),
Ok(Version::new_embedded(2, 0, "Google Nexus".to_string()))
);
assert_eq!(
Version::parse("GLSL ES 1.1"),
Ok(Version::new_embedded(1, 1, String::new()))
);
assert_eq!(
Version::parse("OpenGL ES GLSL ES 3.20"),
Ok(Version::new_embedded(3, 2, String::new()))
);
assert_eq!(
Version::parse("WebGL 2.0 (OpenGL ES 3.0 Chromium)"),
Ok(Version::new_embedded(
3,
0,
"(OpenGL ES 3.0 Chromium)".to_string()
))
);
assert_eq!(
Version::parse("WebGL GLSL ES 3.00 (OpenGL ES GLSL ES 3.0 Chromium)"),
Ok(Version::new_embedded(
3,
0,
"(OpenGL ES GLSL ES 3.0 Chromium)".to_string()
))
);
}
}