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