Skip to main content

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        use vrl::event_path;
99        let mut event = LogEvent::default();
100        event.insert(event_path!("field"), "value");
101
102        let assertions = vec![
103            "type(event) == 'table'",
104            "event.metric == nil",
105            "type(event.log) == 'table'",
106            "event.log.field == 'value'",
107        ];
108
109        assert_event(event.into(), assertions);
110    }
111
112    #[test]
113    fn into_lua_metric() {
114        let event = Event::Metric(Metric::new(
115            "example counter",
116            MetricKind::Absolute,
117            MetricValue::Counter {
118                value: 0.577_215_66,
119            },
120        ));
121
122        let assertions = vec![
123            "type(event) == 'table'",
124            "event.log == nil",
125            "type(event.metric) == 'table'",
126            "event.metric.name == 'example counter'",
127            "event.metric.counter.value == 0.57721566",
128        ];
129
130        assert_event(event, assertions);
131    }
132
133    #[test]
134    fn from_lua_log() {
135        let lua_event = r#"
136        {
137            log = {
138                field = "example",
139                nested = {
140                    field = "another example"
141                }
142            }
143        }"#;
144
145        let event = Lua::new().load(lua_event).eval::<Event>().unwrap();
146        let log = event.as_log();
147        assert_eq!(log["field"], Value::Bytes("example".into()));
148        assert_eq!(log["nested.field"], Value::Bytes("another example".into()));
149    }
150
151    #[test]
152    fn from_lua_metric() {
153        let lua_event = r#"
154        {
155            metric = {
156                name = "example counter",
157                counter = {
158                    value = 0.57721566
159                }
160            }
161        }"#;
162        let expected = Event::Metric(Metric::new(
163            "example counter",
164            MetricKind::Absolute,
165            MetricValue::Counter {
166                value: 0.577_215_66,
167            },
168        ));
169
170        let event = Lua::new().load(lua_event).eval::<Event>().unwrap();
171        vector_common::assert_event_data_eq!(event, expected);
172    }
173
174    #[test]
175    // the panic message a) is platform dependent and b) can change if any code is added before this function.
176    #[allow(clippy::should_panic_without_expect)]
177    #[should_panic]
178    fn from_lua_missing_log_and_metric() {
179        let lua_event = r"{
180            some_field: {}
181        }";
182        Lua::new().load(lua_event).eval::<Event>().unwrap();
183    }
184}