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, Copy, Clone)]
12pub enum ExecutionType {
13    Inbound,
14    Outbound,
15}
16
17pub struct ExecutionResult {
18    pub should_continue: bool,
19    pub execution_context: ExecutionContext,
20}
21
22pub type HostFunctionBuilder =
23    Arc<dyn Fn(&mut Store, &FunctionEnv<ExecutionContext>) -> Function + Send + Sync>;
24pub type HostFunctionMap = HashMap<String, Vec<(String, HostFunctionBuilder)>>;
25
26pub struct WasmRunner<'a> {
27    pub plugin: &'a WasmPlugin,
28    pub execution_type: ExecutionType,
29    host_imports: Option<&'a HostFunctionMap>,
30}
31
32impl<'a> WasmRunner<'a> {
33    pub fn new(
34        plugin: &'a WasmPlugin,
35        execution_type: ExecutionType,
36        host_imports: Option<&'a HostFunctionMap>,
37    ) -> Self {
38        Self {
39            plugin,
40            execution_type,
41            host_imports,
42        }
43    }
44
45    pub fn run(&self, exec_ctx: ExecutionContext) -> Result<ExecutionResult, CardinalError> {
46        // 1) Instantiate a fresh instance per request
47        let mut instance =
48            WasmInstance::from_plugin(self.plugin, self.execution_type, self.host_imports)?;
49
50        {
51            let ctx = instance.env.as_mut(&mut instance.store);
52            match ctx {
53                ExecutionContext::Inbound(inbound) => {
54                    let inbound_ctx = exec_ctx.as_inbound().unwrap().to_owned();
55                    inbound.req_headers = inbound_ctx.req_headers;
56                    inbound.query = inbound_ctx.query;
57                    inbound.body = inbound_ctx.body;
58                }
59                ExecutionContext::Outbound(outbound) => {
60                    let inbound_ctx = exec_ctx.as_outbound().unwrap().to_owned();
61                    outbound.req_headers = inbound_ctx.req_headers;
62                    outbound.query = inbound_ctx.query;
63                    outbound.body = inbound_ctx.body;
64                    outbound.resp_headers = inbound_ctx.resp_headers;
65                    outbound.status = inbound_ctx.status;
66                }
67            }
68        }
69
70        // 3) Get exports
71        let handle: TypedFunction<(i32, i32), i32> = instance
72            .instance
73            .exports
74            .get_typed_function(&instance.store, "handle")
75            .map_err(|e| {
76                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
77                    "missing `handle` export {e}"
78                )))
79            })?;
80
81        let alloc: TypedFunction<(i32, i32), i32> = instance
82            .instance
83            .exports
84            .get_typed_function(&instance.store, "__new")
85            .map_err(|e| {
86                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
87                    "missing `alloc` export {e}"
88                )))
89            })?;
90
91        let body_opt = {
92            let ctx_ref = instance.env.as_ref(&instance.store);
93            ctx_ref.body().clone()
94        };
95
96        let (ptr, len) = if let Some(body) = body_opt.filter(|b| !b.is_empty()) {
97            let len = body.len() as i32;
98
99            let p = alloc.call(&mut instance.store, len, 0).map_err(|e| {
100                CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
101                    "Alloc failed {e}"
102                )))
103            })?;
104
105            {
106                let view = instance.memory.view(&instance.store);
107                view.write(p as u64, &body).map_err(|e| {
108                    CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
109                        "Writing Body failed {e}"
110                    )))
111                })?;
112            }
113
114            (p, len)
115        } else {
116            (0, 0)
117        };
118
119        let decision = handle.call(&mut instance.store, ptr, len).map_err(|e| {
120            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
121                "WASM Handle call failed {e}"
122            )))
123        })?;
124
125        Ok(ExecutionResult {
126            should_continue: decision == 1,
127            execution_context: instance.env.as_ref(&instance.store).clone(),
128        })
129    }
130}