1use scripting::runtime::alloc_string_ref;
12use wasmtime::{ArrayRef, Caller, Linker, Rooted};
13
14use crate::session::SessionData;
15
16pub const PROTOCOL_VERSION: i32 = 1;
20
21pub const REGISTERED_NATIVES: &[&str] = &["rpc-protocol-version", "rpc-session-user-id"];
25
26pub fn register(linker: &mut Linker<SessionData>) -> wasmtime::Result<()> {
30 linker.func_wrap(
31 "nomi",
32 "rpc_protocol_version",
33 |_caller: Caller<'_, SessionData>| -> i32 { PROTOCOL_VERSION },
34 )?;
35 linker.func_wrap(
36 "nomi",
37 "rpc_session_user_id_capture",
38 |mut caller: Caller<'_, SessionData>| -> wasmtime::Result<Option<Rooted<ArrayRef>>> {
39 let uid_string = caller.data().ctx().user_id.to_string();
40 Ok(Some(alloc_string_ref(&mut caller, uid_string.as_bytes())?))
41 },
42 )?;
43 Ok(())
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::ctx::ScriptCtx;
50 use scripting::runtime::{EngineOpts, build_engine, compile_wat};
51 use uuid::Uuid;
52 use wasmtime::Store;
53
54 fn run_module_with_user(uid: Uuid, wat: &str) -> (SessionData, i32) {
55 let engine = build_engine(EngineOpts::baseline().with_fuel()).unwrap();
56 let module = compile_wat(&engine, wat).unwrap();
57 let mut linker: Linker<SessionData> = Linker::new(&engine);
58 register(&mut linker).unwrap();
59 let mut store: Store<SessionData> = Store::new(
60 &engine,
61 SessionData::new(
62 ScriptCtx::new(uid),
63 std::sync::Arc::new(std::sync::Mutex::new(String::new())),
64 ),
65 );
66 store.set_fuel(100_000).unwrap();
67 store.set_epoch_deadline(1);
68 let instance = linker.instantiate(&mut store, &module).unwrap();
69 let func = instance
70 .get_typed_func::<(), i32>(&mut store, "nomi-eval")
71 .unwrap();
72 let result = func.call(&mut store, ()).unwrap();
73 (store.into_data(), result)
74 }
75
76 #[test]
77 fn protocol_version_native_returns_constant() {
78 let wat = r#"
82 (module
83 (import "nomi" "rpc_protocol_version" (func $ver (result i32)))
84 (func (export "nomi-eval") (result i32) (call $ver)))
85 "#;
86 let (_, value) = run_module_with_user(Uuid::nil(), wat);
87 assert_eq!(value, PROTOCOL_VERSION);
88 }
89
90 #[test]
91 fn registered_natives_are_kebab_case() {
92 for name in REGISTERED_NATIVES {
93 assert!(
94 name.chars()
95 .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-'),
96 "non-kebab-case meta native: {name}"
97 );
98 }
99 }
100}