diff options
Diffstat (limited to 'vendor/os_info/src/matcher.rs')
-rw-r--r-- | vendor/os_info/src/matcher.rs | 148 |
1 files changed, 148 insertions, 0 deletions
diff --git a/vendor/os_info/src/matcher.rs b/vendor/os_info/src/matcher.rs new file mode 100644 index 000000000..fcc31bc59 --- /dev/null +++ b/vendor/os_info/src/matcher.rs @@ -0,0 +1,148 @@ +/// An implementation to match on simple strings. +#[derive(Debug, Clone)] +#[allow(dead_code)] +pub enum Matcher { + /// Considers the entire string (trimmed) to be the match. + AllTrimmed, + + /// After finding the `prefix` followed by one or more spaces, returns the following word. + PrefixedWord { prefix: &'static str }, + + /// Similar to `PrefixedWord`, but only if the word is a valid version. + PrefixedVersion { prefix: &'static str }, + + /// Takes a set of lines (separated by `\n`) and searches for the value in a key/value pair + /// separated by the `=` character. For example `VERSION_ID="8.1"`. + KeyValue { key: &'static str }, +} + +impl Matcher { + /// Find the match on the input `string`. + pub fn find(&self, string: &str) -> Option<String> { + match *self { + Self::AllTrimmed => Some(string.trim().to_string()), + Self::PrefixedWord { prefix } => find_prefixed_word(string, prefix).map(str::to_owned), + Self::PrefixedVersion { prefix } => find_prefixed_word(string, prefix) + .filter(|&v| is_valid_version(v)) + .map(str::to_owned), + Self::KeyValue { key } => find_by_key(string, key).map(str::to_owned), + } + } +} + +fn find_by_key<'a>(string: &'a str, key: &str) -> Option<&'a str> { + let key = [key, "="].concat(); + for line in string.lines() { + if line.starts_with(&key) { + return Some(line[key.len()..].trim_matches(|c: char| c == '"' || c.is_whitespace())); + } + } + + None +} + +fn find_prefixed_word<'a>(string: &'a str, prefix: &str) -> Option<&'a str> { + if let Some(prefix_start) = string.find(prefix) { + // Ignore prefix and leading whitespace + let string = &string[prefix_start + prefix.len()..].trim_start(); + + // Find where the word boundary ends + let word_end = string + .find(|c: char| c.is_whitespace()) + .unwrap_or(string.len()); + let string = &string[..word_end]; + + Some(string) + } else { + None + } +} + +fn is_valid_version(word: &str) -> bool { + !word.starts_with('.') && !word.ends_with('.') +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + #[test] + fn trimmed() { + let data = [ + ("", Some("")), + ("test", Some("test")), + (" test", Some("test")), + ("test ", Some("test")), + (" test ", Some("test")), + ]; + + let matcher = Matcher::AllTrimmed; + + for (input, expected) in &data { + let result = matcher.find(input); + assert_eq!(result.as_deref(), *expected); + } + } + + #[test] + fn prefixed_word() { + let data = [ + ("", None), + ("test", Some("")), + ("test1", Some("1")), + ("test 1", Some("1")), + (" test 1", Some("1")), + ("test 1.2.3", Some("1.2.3")), + (" test 1.2.3", Some("1.2.3")), + ]; + + let matcher = Matcher::PrefixedWord { prefix: "test" }; + + for (input, expected) in &data { + let result = matcher.find(input); + assert_eq!(result.as_deref(), *expected); + } + } + + #[test] + fn prefixed_version() { + let data = [ + ("", None), + ("test", Some("")), + ("test 1", Some("1")), + ("test .1", None), + ("test 1.", None), + ("test .1.", None), + (" test 1", Some("1")), + ("test 1.2.3", Some("1.2.3")), + (" test 1.2.3", Some("1.2.3")), + ]; + + let matcher = Matcher::PrefixedVersion { prefix: "test" }; + + for (input, expected) in &data { + let result = matcher.find(input); + assert_eq!(result.as_deref(), *expected); + } + } + + #[test] + fn key_value() { + let data = [ + ("", None), + ("key", None), + ("key=value", Some("value")), + ("key=1", Some("1")), + ("key=\"1\"", Some("1")), + ("key=\"CentOS Linux\"", Some("CentOS Linux")), + ]; + + let matcher = Matcher::KeyValue { key: "key" }; + + for (input, expected) in &data { + let result = matcher.find(input); + assert_eq!(result.as_deref(), *expected); + } + } +} |