1use config::{get_or_create_module, get_or_create_sub_module};
2use mlua::{Lua, MetaMethod, UserData, UserDataMethods};
3use prometheus::{HistogramTimer, HistogramVec};
4use std::sync::LazyLock;
5use tokio::time::Duration;
6
7static LATENCY_HIST: LazyLock<HistogramVec> = LazyLock::new(|| {
8 prometheus::register_histogram_vec!(
9 "user_lua_latency",
10 "how long something user-defined took to run in your lua policy",
11 &["label"]
12 )
13 .unwrap()
14});
15
16pub fn register(lua: &Lua) -> anyhow::Result<()> {
17 let kumo_mod = get_or_create_module(lua, "kumo")?;
18 let time_mod = get_or_create_sub_module(lua, "time")?;
19
20 let sleep_fn = lua.create_async_function(sleep)?;
21 kumo_mod.set("sleep", sleep_fn.clone())?;
22 time_mod.set("sleep", sleep_fn)?;
23
24 time_mod.set("start_timer", lua.create_function(Timer::start)?)?;
25
26 Ok(())
27}
28
29struct Timer {
33 timer: Option<HistogramTimer>,
34}
35
36impl Drop for Timer {
37 fn drop(&mut self) {
38 if let Some(timer) = self.timer.take() {
44 timer.stop_and_discard();
45 }
46 }
47}
48
49impl Timer {
50 fn start(_lua: &Lua, name: String) -> mlua::Result<Self> {
51 let timer = LATENCY_HIST
52 .get_metric_with_label_values(&[&name])
53 .expect("to get histo")
54 .start_timer();
55 Ok(Self { timer: Some(timer) })
56 }
57
58 fn done(_lua: &Lua, this: &mut Self, _: ()) -> mlua::Result<Option<f64>> {
59 Ok(this.timer.take().map(|timer| timer.stop_and_record()))
60 }
61}
62
63impl UserData for Timer {
64 fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
65 methods.add_method_mut("done", Self::done);
66 methods.add_meta_method_mut(MetaMethod::Close, Self::done);
67 }
68}
69
70async fn sleep(_lua: Lua, seconds: f64) -> mlua::Result<()> {
71 tokio::time::sleep(Duration::from_secs_f64(seconds)).await;
72 Ok(())
73}