vector_core/event/lua/
event.rs

1use mlua::prelude::*;
2
3use super::super::{Event, LogEvent, Metric};
4use super::metric::LuaMetric;
5
6pub struct LuaEvent {
7    pub event: Event,
8    pub metric_multi_value_tags: bool,
9}
10
11impl IntoLua for LuaEvent {
12    #![allow(clippy::wrong_self_convention)] // this trait is defined by mlua
13    fn into_lua(self, lua: &Lua) -> LuaResult<LuaValue> {
14        let table = lua.create_table()?;
15        match self.event {
16            Event::Log(log) => table.raw_set("log", log.into_lua(lua)?)?,
17            Event::Metric(metric) => table.raw_set(
18                "metric",
19                LuaMetric {
20                    metric,
21                    multi_value_tags: self.metric_multi_value_tags,
22                }
23                .into_lua(lua)?,
24            )?,
25            Event::Trace(_) => {
26                return Err(LuaError::ToLuaConversionError {
27                    from: String::from("Event"),
28                    to: "table",
29                    message: Some("Trace are not supported".to_string()),
30                })
31            }
32        }
33        Ok(LuaValue::Table(table))
34    }
35}
36
37impl FromLua for Event {
38    fn from_lua(value: LuaValue, lua: &Lua) -> LuaResult<Self> {
39        let LuaValue::Table(table) = &value else {
40            return Err(LuaError::FromLuaConversionError {
41                from: value.type_name(),
42                to: String::from("Event"),
43                message: Some("Event should be a Lua table".to_string()),
44            });
45        };
46        match (table.raw_get("log")?, table.raw_get("metric")?) {
47            (LuaValue::Table(log), LuaValue::Nil) => {
48                Ok(Event::Log(LogEvent::from_lua(LuaValue::Table(log), lua)?))
49            }
50            (LuaValue::Nil, LuaValue::Table(metric)) => Ok(Event::Metric(Metric::from_lua(
51                LuaValue::Table(metric),
52                lua,
53            )?)),
54            _ => Err(LuaError::FromLuaConversionError {
55                from: value.type_name(),
56                to: String::from("Event"),
57                message: Some(
58                    "Event should contain either \"log\" or \"metric\" key at the top level"
59                        .to_string(),
60                ),
61            }),
62        }
63    }
64}
65
66#[cfg(test)]
67mod test {
68    use super::*;
69    use crate::event::{
70        metric::{MetricKind, MetricValue},
71        Metric, Value,
72    };
73
74    fn assert_event(event: Event, assertions: Vec<&'static str>) {
75        let lua = Lua::new();
76        lua.globals()
77            .set(
78                "event",
79                LuaEvent {
80                    event,
81                    metric_multi_value_tags: false,
82                },
83            )
84            .unwrap();
85        for assertion in assertions {
86            assert!(
87                lua.load(assertion).eval::<bool>().expect(assertion),
88                "{}",
89                assertion
90            );
91        }
92    }
93
94    #[test]
95    fn into_lua_log() {
96        let mut event = LogEvent::default();
97        event.insert("field", "value");
98
99        let assertions = vec![
100            "type(event) == 'table'",
101            "event.metric == nil",
102            "type(event.log) == 'table'",
103            "event.log.field == 'value'",
104        ];
105
106        assert_event(event.into(), assertions);
107    }
108
109    #[test]
110    fn into_lua_metric() {
111        let event = Event::Metric(Metric::new(
112            "example counter",
113            MetricKind::Absolute,
114            MetricValue::Counter {
115                value: 0.577_215_66,
116            },
117        ));
118
119        let assertions = vec![
120            "type(event) == 'table'",
121            "event.log == nil",
122            "type(event.metric) == 'table'",
123            "event.metric.name == 'example counter'",
124            "event.metric.counter.value == 0.57721566",
125        ];
126
127        assert_event(event, assertions);
128    }
129
130    #[test]
131    fn from_lua_log() {
132        let lua_event = r#"
133        {
134            log = {
135                field = "example",
136                nested = {
137                    field = "another example"
138                }
139            }
140        }"#;
141
142        let event = Lua::new().load(lua_event).eval::<Event>().unwrap();
143        let log = event.as_log();
144        assert_eq!(log["field"], Value::Bytes("example".into()));
145        assert_eq!(log["nested.field"], Value::Bytes("another example".into()));
146    }
147
148    #[test]
149    fn from_lua_metric() {
150        let lua_event = r#"
151        {
152            metric = {
153                name = "example counter",
154                counter = {
155                    value = 0.57721566
156                }
157            }
158        }"#;
159        let expected = Event::Metric(Metric::new(
160            "example counter",
161            MetricKind::Absolute,
162            MetricValue::Counter {
163                value: 0.577_215_66,
164            },
165        ));
166
167        let event = Lua::new().load(lua_event).eval::<Event>().unwrap();
168        vector_common::assert_event_data_eq!(event, expected);
169    }
170
171    #[test]
172    // the panic message a) is platform dependent and b) can change if any code is added before this function.
173    #[allow(clippy::should_panic_without_expect)]
174    #[should_panic]
175    fn from_lua_missing_log_and_metric() {
176        let lua_event = r"{
177            some_field: {}
178        }";
179        Lua::new().load(lua_event).eval::<Event>().unwrap();
180    }
181}