mod json;
mod kv;
mod map;
mod plain;
pub use json::JsonEvent;
pub use kv::KvEvent;
pub use map::MapEvent;
pub use plain::PlainEvent;
use std::borrow::Cow;
use serde_json::Value;
#[derive(Debug, Clone, PartialEq)]
pub enum EventValue<'a> {
Str(Cow<'a, str>),
Int(i64),
Float(f64),
Bool(bool),
Null,
Array(Vec<EventValue<'a>>),
Map(Vec<(Cow<'a, str>, EventValue<'a>)>),
}
impl<'a> EventValue<'a> {
#[inline]
pub fn as_str(&self) -> Option<Cow<'_, str>> {
match self {
EventValue::Str(s) => Some(Cow::Borrowed(s)),
EventValue::Int(n) => Some(Cow::Owned(n.to_string())),
EventValue::Float(f) => Some(Cow::Owned(f.to_string())),
EventValue::Bool(b) => Some(Cow::Borrowed(if *b { "true" } else { "false" })),
_ => None,
}
}
#[inline]
pub fn as_f64(&self) -> Option<f64> {
match self {
EventValue::Float(f) => Some(*f),
EventValue::Int(n) => Some(*n as f64),
EventValue::Str(s) => s.parse().ok(),
_ => None,
}
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
match self {
EventValue::Int(n) => Some(*n),
EventValue::Float(f) => {
let truncated = *f as i64;
if (truncated as f64 - f).abs() < f64::EPSILON {
Some(truncated)
} else {
None
}
}
EventValue::Str(s) => s.parse().ok(),
_ => None,
}
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
match self {
EventValue::Bool(b) => Some(*b),
EventValue::Str(s) => match s.to_lowercase().as_str() {
"true" | "1" | "yes" => Some(true),
"false" | "0" | "no" => Some(false),
_ => None,
},
_ => None,
}
}
#[inline]
pub fn is_null(&self) -> bool {
matches!(self, EventValue::Null)
}
pub fn to_json(&self) -> Value {
match self {
EventValue::Str(s) => Value::String(s.to_string()),
EventValue::Int(n) => Value::Number((*n).into()),
EventValue::Float(f) => {
serde_json::Number::from_f64(*f).map_or(Value::Null, Value::Number)
}
EventValue::Bool(b) => Value::Bool(*b),
EventValue::Null => Value::Null,
EventValue::Array(arr) => Value::Array(arr.iter().map(|v| v.to_json()).collect()),
EventValue::Map(entries) => {
let map = entries
.iter()
.map(|(k, v)| (k.to_string(), v.to_json()))
.collect();
Value::Object(map)
}
}
}
}
impl<'a> From<&'a Value> for EventValue<'a> {
fn from(v: &'a Value) -> Self {
match v {
Value::String(s) => EventValue::Str(Cow::Borrowed(s.as_str())),
Value::Number(n) => {
if let Some(i) = n.as_i64() {
EventValue::Int(i)
} else {
EventValue::Float(n.as_f64().unwrap_or(f64::NAN))
}
}
Value::Bool(b) => EventValue::Bool(*b),
Value::Null => EventValue::Null,
Value::Array(arr) => EventValue::Array(arr.iter().map(EventValue::from).collect()),
Value::Object(map) => EventValue::Map(
map.iter()
.map(|(k, v)| (Cow::Borrowed(k.as_str()), EventValue::from(v)))
.collect(),
),
}
}
}
pub trait Event {
fn get_field(&self, path: &str) -> Option<EventValue<'_>>;
fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool;
fn all_string_values(&self) -> Vec<Cow<'_, str>>;
fn to_json(&self) -> Value;
}
impl<T: Event + ?Sized> Event for &T {
fn get_field(&self, path: &str) -> Option<EventValue<'_>> {
(**self).get_field(path)
}
fn any_string_value(&self, pred: &dyn Fn(&str) -> bool) -> bool {
(**self).any_string_value(pred)
}
fn all_string_values(&self) -> Vec<Cow<'_, str>> {
(**self).all_string_values()
}
fn to_json(&self) -> Value {
(**self).to_json()
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn event_value_as_str() {
assert_eq!(EventValue::Str(Cow::Borrowed("hi")).as_str().unwrap(), "hi");
assert_eq!(EventValue::Int(42).as_str().unwrap(), "42");
assert_eq!(EventValue::Float(2.71).as_str().unwrap(), "2.71");
assert_eq!(EventValue::Bool(true).as_str().unwrap(), "true");
assert!(EventValue::Null.as_str().is_none());
}
#[test]
fn event_value_as_f64() {
assert_eq!(EventValue::Float(2.71).as_f64(), Some(2.71));
assert_eq!(EventValue::Int(42).as_f64(), Some(42.0));
assert_eq!(EventValue::Str(Cow::Borrowed("1.5")).as_f64(), Some(1.5));
assert!(EventValue::Bool(true).as_f64().is_none());
}
#[test]
fn event_value_as_i64() {
assert_eq!(EventValue::Int(42).as_i64(), Some(42));
assert_eq!(EventValue::Float(42.0).as_i64(), Some(42));
assert_eq!(EventValue::Float(42.5).as_i64(), None);
assert_eq!(EventValue::Str(Cow::Borrowed("100")).as_i64(), Some(100));
}
#[test]
fn event_value_to_json() {
assert_eq!(EventValue::Str(Cow::Borrowed("hi")).to_json(), json!("hi"));
assert_eq!(EventValue::Int(42).to_json(), json!(42));
assert_eq!(EventValue::Bool(true).to_json(), json!(true));
assert_eq!(EventValue::Null.to_json(), Value::Null);
}
}