vector_core/event/lua/
event.rs

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