cardinal_wasm_plugins/
runner.rs

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