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