use rhai::{Dynamic, Engine, ImmutableString, Map};
pub fn path_equals(event: Map, path: ImmutableString, expected: Dynamic) -> bool {
let path_str = path.as_str();
let parts: Vec<&str> = path_str.split('.').collect();
let mut current_map = event;
for (i, part) in parts.iter().enumerate() {
if let Some(value) = current_map.get(*part).cloned() {
if i == parts.len() - 1 {
return value.type_name() == expected.type_name()
&& value.to_string() == expected.to_string();
} else {
if let Some(nested_map) = value.read_lock::<Map>() {
current_map = nested_map.clone();
} else {
return false;
}
}
} else {
return false;
}
}
false
}
pub fn to_number(value: Dynamic, default: Dynamic) -> Dynamic {
if let Ok(num) = value.as_int() {
return Dynamic::from(num);
}
if let Ok(num) = value.as_float() {
return Dynamic::from(num);
}
if let Some(s) = value.read_lock::<ImmutableString>() {
if let Ok(num) = s.parse::<i64>() {
return Dynamic::from(num);
}
if let Ok(num) = s.parse::<f64>() {
return Dynamic::from(num);
}
}
default
}
pub fn to_bool(value: Dynamic, default: Dynamic) -> Dynamic {
if let Ok(b) = value.as_bool() {
return Dynamic::from(b);
}
if let Some(s) = value.read_lock::<ImmutableString>() {
let s_lower = s.to_lowercase();
match s_lower.as_str() {
"true" | "yes" | "1" | "on" => return Dynamic::from(true),
"false" | "no" | "0" | "off" => return Dynamic::from(false),
_ => {}
}
}
if let Ok(num) = value.as_int() {
return Dynamic::from(num != 0);
}
if let Ok(num) = value.as_float() {
return Dynamic::from(num != 0.0);
}
default
}
pub fn register_functions(engine: &mut Engine) {
engine.register_fn("path_equals", path_equals);
engine.register_fn("to_number", to_number);
engine.register_fn("to_bool", to_bool);
}
#[cfg(test)]
mod tests {
use super::*;
use rhai::Map;
fn create_test_event() -> Map {
let mut event = Map::new();
event.insert("name".into(), Dynamic::from("alice"));
event.insert("score".into(), Dynamic::from(85));
let mut user = Map::new();
user.insert("role".into(), Dynamic::from("admin"));
user.insert("active".into(), Dynamic::from(true));
event.insert("user".into(), Dynamic::from(user));
event
}
#[test]
fn test_path_equals() {
let event = create_test_event();
assert!(path_equals(
event.clone(),
"name".into(),
Dynamic::from("alice")
));
assert!(path_equals(
event.clone(),
"user.role".into(),
Dynamic::from("admin")
));
assert!(!path_equals(
event.clone(),
"name".into(),
Dynamic::from("bob")
));
assert!(!path_equals(
event,
"missing".into(),
Dynamic::from("anything")
));
}
#[test]
fn test_to_number() {
let result = to_number(Dynamic::from(42i64), Dynamic::from(0i64));
assert_eq!(result.as_int().unwrap(), 42i64);
let result = to_number(Dynamic::from(3.14), Dynamic::from(0.0));
assert_eq!(result.as_float().unwrap(), 3.14);
let result = to_number(Dynamic::from("123"), Dynamic::from(0i64));
assert_eq!(result.as_int().unwrap(), 123i64);
let result = to_number(Dynamic::from("12.5"), Dynamic::from(0.0));
assert_eq!(result.as_float().unwrap(), 12.5);
let result = to_number(Dynamic::from("invalid"), Dynamic::from(999i64));
assert_eq!(result.as_int().unwrap(), 999i64);
}
#[test]
fn test_to_bool() {
assert!(to_bool(Dynamic::from(true), Dynamic::from(false))
.as_bool()
.unwrap());
assert!(to_bool(Dynamic::from("yes"), Dynamic::from(false))
.as_bool()
.unwrap());
assert!(to_bool(Dynamic::from("1"), Dynamic::from(false))
.as_bool()
.unwrap());
assert!(!to_bool(Dynamic::from("false"), Dynamic::from(true))
.as_bool()
.unwrap());
assert!(to_bool(Dynamic::from(1i64), Dynamic::from(false))
.as_bool()
.unwrap());
assert!(!to_bool(Dynamic::from(0i64), Dynamic::from(true))
.as_bool()
.unwrap());
assert!(to_bool(Dynamic::from("invalid"), Dynamic::from(true))
.as_bool()
.unwrap());
}
}