cardinal_wasm_plugins/
runner.rs

1use crate::instance::WasmInstance;
2use crate::plugin::WasmPlugin;
3use crate::{ExecutionContext, SharedExecutionContext};
4use bytes::Bytes;
5use cardinal_errors::internal::CardinalInternalError;
6use cardinal_errors::CardinalError;
7use std::collections::HashMap;
8use std::sync::Arc;
9use wasmer::TypedFunction;
10use wasmer::{Function, FunctionEnv, Store};
11
12#[derive(Debug)]
13pub struct ExecutionResult {
14    pub should_continue: bool,
15    pub execution_context: ExecutionContext,
16}
17
18pub type HostFunctionBuilder =
19    Arc<dyn Fn(&mut Store, &FunctionEnv<SharedExecutionContext>) -> Function + Send + Sync>;
20pub type HostFunctionMap = HashMap<String, Vec<(String, HostFunctionBuilder)>>;
21
22pub struct WasmRunner<'a> {
23    pub plugin: &'a WasmPlugin,
24    host_imports: Option<&'a HostFunctionMap>,
25}
26
27impl<'a> WasmRunner<'a> {
28    pub fn new(plugin: &'a WasmPlugin, host_imports: Option<&'a HostFunctionMap>) -> Self {
29        Self {
30            plugin,
31            host_imports,
32        }
33    }
34
35    pub fn run(
36        &self,
37        shared_ctx: SharedExecutionContext,
38    ) -> Result<ExecutionResult, CardinalError> {
39        // 1) Instantiate a fresh instance per request
40        let mut instance =
41            WasmInstance::from_plugin(self.plugin, self.host_imports, shared_ctx.clone())?;
42
43        // I don't think we need this anymore
44        // {
45        //     let ctx = instance.env.as_mut(&mut instance.store);
46        //     let memory = ctx.memory().clone();
47        //     *ctx = exec_ctx;
48        //     *ctx.memory_mut() = memory;
49        // }
50
51        // 3) Get exports
52        let handle: TypedFunction<(i32, i32), i32> = instance
53            .instance
54            .exports
55            .get_typed_function(&instance.store, "handle")
56            .map_err(|e| {
57                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
58                    "missing `handle` export {e}"
59                )))
60            })?;
61
62        let alloc: TypedFunction<(i32, i32), i32> = instance
63            .instance
64            .exports
65            .get_typed_function(&instance.store, "__new")
66            .map_err(|e| {
67                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
68                    "missing `alloc` export {e}"
69                )))
70            })?;
71
72        let body_opt: Option<Bytes> = {
73            let ctx_ref = instance.env.as_ref(&instance.store);
74            ctx_ref.read().body().clone()
75        };
76
77        let (ptr, len) = if let Some(body) = body_opt.filter(|b| !b.is_empty()) {
78            let len = body.len() as i32;
79
80            let p = alloc.call(&mut instance.store, len, 0).map_err(|e| {
81                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
82                    "Alloc failed {e}"
83                )))
84            })?;
85
86            {
87                let view = instance.memory.view(&instance.store);
88                view.write(p as u64, &body).map_err(|e| {
89                    CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
90                        "Writing Body failed {e}"
91                    )))
92                })?;
93            }
94
95            (p, len)
96        } else {
97            (0, 0)
98        };
99
100        let decision = handle.call(&mut instance.store, ptr, len).map_err(|e| {
101            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
102                "WASM Handle call failed {e}"
103            )))
104        })?;
105
106        let exec_ctx_clone = shared_ctx.read().clone();
107
108        Ok(ExecutionResult {
109            should_continue: decision == 1,
110            execution_context: exec_ctx_clone,
111        })
112    }
113}