os_info/
matcher.rs
1#[derive(Debug, Clone)]
3#[allow(dead_code)]
4pub enum Matcher {
5 AllTrimmed,
7
8 PrefixedWord { prefix: &'static str },
10
11 PrefixedVersion { prefix: &'static str },
13
14 KeyValue { key: &'static str },
17}
18
19impl Matcher {
20 pub fn find(&self, string: &str) -> Option<String> {
22 match *self {
23 Self::AllTrimmed => Some(string.trim().to_string()),
24 Self::PrefixedWord { prefix } => find_prefixed_word(string, prefix).map(str::to_owned),
25 Self::PrefixedVersion { prefix } => find_prefixed_word(string, prefix)
26 .filter(|&v| is_valid_version(v))
27 .map(str::to_owned),
28 Self::KeyValue { key } => find_by_key(string, key).map(str::to_owned),
29 }
30 }
31}
32
33fn find_by_key<'a>(string: &'a str, key: &str) -> Option<&'a str> {
34 let key = [key, "="].concat();
35 for line in string.lines() {
36 if line.starts_with(&key) {
37 return Some(line[key.len()..].trim_matches(|c: char| c == '"' || c.is_whitespace()));
38 }
39 }
40
41 None
42}
43
44fn find_prefixed_word<'a>(string: &'a str, prefix: &str) -> Option<&'a str> {
45 if let Some(prefix_start) = string.find(prefix) {
46 let string = &string[prefix_start + prefix.len()..].trim_start();
48
49 let word_end = string
51 .find(|c: char| c.is_whitespace())
52 .unwrap_or(string.len());
53 let string = &string[..word_end];
54
55 Some(string)
56 } else {
57 None
58 }
59}
60
61fn is_valid_version(word: &str) -> bool {
62 !word.starts_with('.') && !word.ends_with('.')
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use pretty_assertions::assert_eq;
69
70 #[test]
71 fn trimmed() {
72 let data = [
73 ("", Some("")),
74 ("test", Some("test")),
75 (" test", Some("test")),
76 ("test ", Some("test")),
77 (" test ", Some("test")),
78 ];
79
80 let matcher = Matcher::AllTrimmed;
81
82 for (input, expected) in &data {
83 let result = matcher.find(input);
84 assert_eq!(result.as_deref(), *expected);
85 }
86 }
87
88 #[test]
89 fn prefixed_word() {
90 let data = [
91 ("", None),
92 ("test", Some("")),
93 ("test1", Some("1")),
94 ("test 1", Some("1")),
95 (" test 1", Some("1")),
96 ("test 1.2.3", Some("1.2.3")),
97 (" test 1.2.3", Some("1.2.3")),
98 ];
99
100 let matcher = Matcher::PrefixedWord { prefix: "test" };
101
102 for (input, expected) in &data {
103 let result = matcher.find(input);
104 assert_eq!(result.as_deref(), *expected);
105 }
106 }
107
108 #[test]
109 fn prefixed_version() {
110 let data = [
111 ("", None),
112 ("test", Some("")),
113 ("test 1", Some("1")),
114 ("test .1", None),
115 ("test 1.", None),
116 ("test .1.", None),
117 (" test 1", Some("1")),
118 ("test 1.2.3", Some("1.2.3")),
119 (" test 1.2.3", Some("1.2.3")),
120 ];
121
122 let matcher = Matcher::PrefixedVersion { prefix: "test" };
123
124 for (input, expected) in &data {
125 let result = matcher.find(input);
126 assert_eq!(result.as_deref(), *expected);
127 }
128 }
129
130 #[test]
131 fn key_value() {
132 let data = [
133 ("", None),
134 ("key", None),
135 ("key=value", Some("value")),
136 ("key=1", Some("1")),
137 ("key=\"1\"", Some("1")),
138 ("key=\"CentOS Linux\"", Some("CentOS Linux")),
139 ];
140
141 let matcher = Matcher::KeyValue { key: "key" };
142
143 for (input, expected) in &data {
144 let result = matcher.find(input);
145 assert_eq!(result.as_deref(), *expected);
146 }
147 }
148}