1mod json;
11mod kv;
12mod map;
13mod plain;
14
15pub use json::JsonEvent;
16pub use kv::KvEvent;
17pub use map::MapEvent;
18pub use plain::PlainEvent;
19
20use std::borrow::Cow;
21
22use serde_json::Value;
23
24#[derive(Debug, Clone, PartialEq)]
34pub enum EventValue<'a> {
35 Str(Cow<'a, str>),
36 Int(i64),
37 Float(f64),
38 Bool(bool),
39 Null,
40 Array(Vec<EventValue<'a>>),
41 Map(Vec<(Cow<'a, str>, EventValue<'a>)>),
42}
43
44impl<'a> EventValue<'a> {
45 #[inline]
47 pub fn as_str(&self) -> Option<Cow<'_, str>> {
48 match self {
49 EventValue::Str(s) => Some(Cow::Borrowed(s)),
50 EventValue::Int(n) => Some(Cow::Owned(n.to_string())),
51 EventValue::Float(f) => Some(Cow::Owned(f.to_string())),
52 EventValue::Bool(b) => Some(Cow::Borrowed(if *b { "true" } else { "false" })),
53 _ => None,
54 }
55 }
56
57 #[inline]
59 pub fn as_f64(&self) -> Option<f64> {
60 match self {
61 EventValue::Float(f) => Some(*f),
62 EventValue::Int(n) => Some(*n as f64),
63 EventValue::Str(s) => s.parse().ok(),
64 _ => None,
65 }
66 }
67
68 #[inline]
70 pub fn as_i64(&self) -> Option<i64> {
71 match self {
72 EventValue::Int(n) => Some(*n),
73 EventValue::Float(f) => {
74 let truncated = *f as i64;
75 if (truncated as f64 - f).abs() < f64::EPSILON {
76 Some(truncated)
77 } else {
78 None
79 }
80 }
81 EventValue::Str(s) => s.parse().ok(),
82 _ => None,
83 }
84 }
85
86 #[inline]
88 pub fn as_bool(&self) -> Option<bool> {
89 match self {
90 EventValue::Bool(b) => Some(*b),
91 EventValue::Str(s) => match s.to_lowercase().as_str() {
92 "true" | "1" | "yes" => Some(true),
93 "false" | "0" | "no" => Some(false),
94 _ => None,
95 },
96 _ => None,
97 }
98 }
99
100 #[inline]
101 pub fn is_null(&self) -> bool {
102 matches!(self, EventValue::Null)
103 }
104
105 pub fn to_json(&self) -> Value {
107 match self {
108 EventValue::Str(s) => Value::String(s.to_string()),
109 EventValue::Int(n) => Value::Number((*n).into()),
110 EventValue::Float(f) => {
111 serde_json::Number::from_f64(*f).map_or(Value::Null, Value::Number)
112 }
113 EventValue::Bool(b) => Value::Bool(*b),
114 EventValue::Null => Value::Null,
115 EventValue::Array(arr) => Value::Array(arr.iter().map(|v| v.to_json()).collect()),
116 EventValue::Map(entries) => {
117 let map = entries
118 .iter()
119 .map(|(k, v)| (k.to_string(), v.to_json()))
120 .collect();
121 Value::Object(map)
122 }
123 }
124 }
125}
126
127impl<'a> From<&'a Value> for EventValue<'a> {
128 fn from(v: &'a Value) -> Self {
129 match v {
130 Value::String(s) => EventValue::Str(Cow::Borrowed(s.as_str())),
131 Value::Number(n) => {
132 if let Some(i) = n.as_i64() {
133 EventValue::Int(i)
134 } else {
135 EventValue::Float(n.as_f64().unwrap_or(f64::NAN))
136 }
137 }
138 Value::Bool(b) => EventValue::Bool(*b),
139 Value::Null => EventValue::Null,
140 Value::Array(arr) => EventValue::Array(arr.iter().map(EventValue::from).collect()),
141 Value::Object(map) => EventValue::Map(
142 map.iter()
143 .map(|(k, v)| (Cow::Borrowed(k.as_str()), EventValue::from(v)))
144 .collect(),
145 ),
146 }
147 }
148}
149
150pub trait Event {
159 fn get_field(&self, path: &str) -> Option<EventValue<'_>>;
164
165 fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool;
168
169 fn all_string_values(&self) -> Vec<Cow<'_, str>>;
171
172 fn to_json(&self) -> Value;
174
175 fn field_keys(&self) -> Vec<Cow<'_, str>> {
189 let mut paths: Vec<String> = Vec::new();
190 collect_field_keys_json(&self.to_json(), "", &mut paths);
191 paths.into_iter().map(Cow::Owned).collect()
192 }
193}
194
195pub(crate) const FIELD_KEYS_MAX_DEPTH: usize = 64;
199
200pub(crate) fn collect_field_keys_json(value: &Value, prefix: &str, out: &mut Vec<String>) {
205 collect_field_keys_json_depth(value, prefix, out, FIELD_KEYS_MAX_DEPTH);
206}
207
208fn collect_field_keys_json_depth(value: &Value, prefix: &str, out: &mut Vec<String>, depth: usize) {
209 if depth == 0 {
210 return;
211 }
212 if let Value::Object(map) = value {
213 for (k, v) in map {
214 let path = if prefix.is_empty() {
215 k.clone()
216 } else {
217 format!("{prefix}.{k}")
218 };
219 match v {
220 Value::Object(_) => collect_field_keys_json_depth(v, &path, out, depth - 1),
223 _ => out.push(path),
224 }
225 }
226 }
227}
228
229impl<T: Event + ?Sized> Event for &T {
230 fn get_field(&self, path: &str) -> Option<EventValue<'_>> {
231 (**self).get_field(path)
232 }
233
234 fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool {
235 (**self).any_string_value(pred)
236 }
237
238 fn all_string_values(&self) -> Vec<Cow<'_, str>> {
239 (**self).all_string_values()
240 }
241
242 fn to_json(&self) -> Value {
243 (**self).to_json()
244 }
245
246 fn field_keys(&self) -> Vec<Cow<'_, str>> {
247 (**self).field_keys()
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use serde_json::json;
255
256 #[test]
257 fn event_value_as_str() {
258 assert_eq!(EventValue::Str(Cow::Borrowed("hi")).as_str().unwrap(), "hi");
259 assert_eq!(EventValue::Int(42).as_str().unwrap(), "42");
260 assert_eq!(EventValue::Float(2.71).as_str().unwrap(), "2.71");
261 assert_eq!(EventValue::Bool(true).as_str().unwrap(), "true");
262 assert!(EventValue::Null.as_str().is_none());
263 }
264
265 #[test]
266 fn event_value_as_f64() {
267 assert_eq!(EventValue::Float(2.71).as_f64(), Some(2.71));
268 assert_eq!(EventValue::Int(42).as_f64(), Some(42.0));
269 assert_eq!(EventValue::Str(Cow::Borrowed("1.5")).as_f64(), Some(1.5));
270 assert!(EventValue::Bool(true).as_f64().is_none());
271 }
272
273 #[test]
274 fn event_value_as_i64() {
275 assert_eq!(EventValue::Int(42).as_i64(), Some(42));
276 assert_eq!(EventValue::Float(42.0).as_i64(), Some(42));
277 assert_eq!(EventValue::Float(42.5).as_i64(), None);
278 assert_eq!(EventValue::Str(Cow::Borrowed("100")).as_i64(), Some(100));
279 }
280
281 #[test]
282 fn event_value_to_json() {
283 assert_eq!(EventValue::Str(Cow::Borrowed("hi")).to_json(), json!("hi"));
284 assert_eq!(EventValue::Int(42).to_json(), json!(42));
285 assert_eq!(EventValue::Bool(true).to_json(), json!(true));
286 assert_eq!(EventValue::Null.to_json(), Value::Null);
287 }
288}