1use serde_json::Value;
7
8#[derive(Debug)]
13pub struct Event<'a> {
14 inner: &'a Value,
15}
16
17impl<'a> Event<'a> {
18 pub fn from_value(value: &'a Value) -> Self {
20 Event { inner: value }
21 }
22
23 pub fn get_field(&self, path: &str) -> Option<&'a Value> {
29 if let Some(obj) = self.inner.as_object()
31 && let Some(v) = obj.get(path)
32 {
33 return Some(v);
34 }
35
36 if path.contains('.') {
38 let parts: Vec<&str> = path.split('.').collect();
39 return traverse(self.inner, &parts);
40 }
41
42 None
43 }
44
45 pub fn all_string_values(&self) -> Vec<&'a str> {
51 let mut values = Vec::new();
52 collect_string_values(self.inner, &mut values, MAX_NESTING_DEPTH);
53 values
54 }
55
56 pub fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool {
61 any_string_value_rec(self.inner, pred, MAX_NESTING_DEPTH)
62 }
63
64 pub fn as_value(&self) -> &'a Value {
66 self.inner
67 }
68}
69
70fn traverse<'a>(current: &'a Value, parts: &[&str]) -> Option<&'a Value> {
75 if parts.is_empty() {
76 return Some(current);
77 }
78
79 let (head, rest) = (parts[0], &parts[1..]);
80
81 match current {
82 Value::Object(map) => {
83 let next = map.get(head)?;
84 traverse(next, rest)
85 }
86 Value::Array(arr) => {
87 for item in arr {
89 if let Some(v) = traverse(item, parts) {
90 return Some(v);
91 }
92 }
93 None
94 }
95 _ => None,
96 }
97}
98
99const MAX_NESTING_DEPTH: usize = 64;
101
102fn any_string_value_rec(v: &Value, pred: &dyn Fn(&str) -> bool, depth: usize) -> bool {
103 if depth == 0 {
104 return false;
105 }
106 match v {
107 Value::String(s) => pred(s.as_str()),
108 Value::Object(map) => map
109 .values()
110 .any(|val| any_string_value_rec(val, pred, depth - 1)),
111 Value::Array(arr) => arr
112 .iter()
113 .any(|val| any_string_value_rec(val, pred, depth - 1)),
114 _ => false,
115 }
116}
117
118fn collect_string_values<'a>(v: &'a Value, out: &mut Vec<&'a str>, depth: usize) {
119 if depth == 0 {
120 return;
121 }
122 match v {
123 Value::String(s) => out.push(s.as_str()),
124 Value::Object(map) => {
125 for val in map.values() {
126 collect_string_values(val, out, depth - 1);
127 }
128 }
129 Value::Array(arr) => {
130 for val in arr {
131 collect_string_values(val, out, depth - 1);
132 }
133 }
134 _ => {}
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use serde_json::json;
142
143 #[test]
144 fn test_flat_field() {
145 let v = json!({"CommandLine": "whoami", "User": "admin"});
146 let event = Event::from_value(&v);
147 assert_eq!(
148 event.get_field("CommandLine"),
149 Some(&Value::String("whoami".into()))
150 );
151 }
152
153 #[test]
154 fn test_nested_field() {
155 let v = json!({"actor": {"id": "user123", "type": "User"}});
156 let event = Event::from_value(&v);
157 assert_eq!(
158 event.get_field("actor.id"),
159 Some(&Value::String("user123".into()))
160 );
161 }
162
163 #[test]
164 fn test_flat_key_precedence() {
165 let v = json!({"actor.id": "flat_value", "actor": {"id": "nested_value"}});
167 let event = Event::from_value(&v);
168 assert_eq!(
169 event.get_field("actor.id"),
170 Some(&Value::String("flat_value".into()))
171 );
172 }
173
174 #[test]
175 fn test_missing_field() {
176 let v = json!({"foo": "bar"});
177 let event = Event::from_value(&v);
178 assert_eq!(event.get_field("missing"), None);
179 }
180
181 #[test]
182 fn test_array_traversal() {
183 let v = json!({"a": {"b": [{"c": "found"}, {"c": "other"}]}});
185 let event = Event::from_value(&v);
186 assert_eq!(
187 event.get_field("a.b.c"),
188 Some(&Value::String("found".into()))
189 );
190 }
191
192 #[test]
193 fn test_array_traversal_no_match() {
194 let v = json!({"a": {"b": [{"x": 1}, {"y": 2}]}});
196 let event = Event::from_value(&v);
197 assert_eq!(event.get_field("a.b.c"), None);
198 }
199
200 #[test]
201 fn test_array_traversal_deep() {
202 let v = json!({
204 "events": [
205 {"actors": [{"name": "alice"}, {"name": "bob"}]},
206 {"actors": [{"name": "charlie"}]}
207 ]
208 });
209 let event = Event::from_value(&v);
210 assert_eq!(
212 event.get_field("events.actors.name"),
213 Some(&Value::String("alice".into()))
214 );
215 }
216
217 #[test]
218 fn test_array_at_root_level() {
219 let v = json!({"process": [{"command_line": "whoami"}, {"command_line": "id"}]});
221 let event = Event::from_value(&v);
222 assert_eq!(
223 event.get_field("process.command_line"),
224 Some(&Value::String("whoami".into()))
225 );
226 }
227
228 #[test]
229 fn test_array_returns_array_value() {
230 let v = json!({"a": {"tags": ["t1", "t2"]}});
232 let event = Event::from_value(&v);
233 assert_eq!(event.get_field("a.tags"), Some(&json!(["t1", "t2"])));
234 }
235
236 #[test]
237 fn test_flat_key_still_wins_over_array_traversal() {
238 let v = json!({"a.b.c": "flat", "a": {"b": [{"c": "nested"}]}});
240 let event = Event::from_value(&v);
241 assert_eq!(
242 event.get_field("a.b.c"),
243 Some(&Value::String("flat".into()))
244 );
245 }
246
247 #[test]
248 fn test_all_string_values() {
249 let v = json!({
250 "a": "hello",
251 "b": 42,
252 "c": {"d": "world", "e": true},
253 "f": ["one", "two"]
254 });
255 let event = Event::from_value(&v);
256 let values = event.all_string_values();
257 assert!(values.contains(&"hello"));
258 assert!(values.contains(&"world"));
259 assert!(values.contains(&"one"));
260 assert!(values.contains(&"two"));
261 assert_eq!(values.len(), 4);
262 }
263}