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