cardinal_wasm_plugins/
plugin.rs

1use cardinal_errors::internal::CardinalInternalError;
2use cardinal_errors::CardinalError;
3use std::collections::HashSet;
4use std::path::{Path, PathBuf};
5use wasmer::{Engine, Module};
6
7pub struct WasmPlugin {
8    pub engine: Engine,
9    pub module: Module,
10    pub path: PathBuf,
11    pub memory_name: String,
12    pub handle_name: String,
13}
14
15impl WasmPlugin {
16    pub fn new(
17        engine: Engine,
18        module: Module,
19        memory_name: Option<String>,
20        handle_name: Option<String>,
21    ) -> Result<Self, CardinalError> {
22        let memory_name = memory_name.unwrap_or_else(|| "memory".to_string());
23        let handle_name = handle_name.unwrap_or_else(|| "handle".to_string());
24
25        let plugin = Self {
26            engine,
27            module,
28            path: PathBuf::new(),
29            memory_name: memory_name.clone(),
30            handle_name: handle_name.clone(),
31        };
32
33        plugin.validate_exports(&[memory_name, handle_name])?;
34
35        Ok(plugin)
36    }
37
38    /// Load & compile a Wasm module from a file path.
39    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, CardinalError> {
40        let path = path.as_ref().to_path_buf();
41        let bytes = std::fs::read(&path)?;
42        let (engine, module) = Self::initiate(&bytes, None)?;
43        let mut plugin = Self::new(engine, module, None, None)?;
44        plugin.path = path;
45
46        Ok(plugin)
47    }
48
49    pub fn initiate(
50        bytes: &[u8],
51        engine: Option<Engine>,
52    ) -> Result<(Engine, Module), CardinalError> {
53        let engine = engine.unwrap_or_default();
54        let module = Module::new(&engine, bytes).map_err(|e| {
55            CardinalError::InternalError(CardinalInternalError::InvalidWasmModule(format!(
56                "Error initiating plugin {}",
57                e
58            )))
59        })?;
60
61        Ok((engine, module))
62    }
63
64    pub fn with_memory_name(mut self, name: String) -> Self {
65        self.memory_name = name;
66        self
67    }
68
69    pub fn with_handle_name(mut self, name: String) -> Self {
70        self.handle_name = name;
71        self
72    }
73
74    pub fn validate_exports<I, S>(&self, required: I) -> Result<(), CardinalError>
75    where
76        I: IntoIterator<Item = S>,
77        S: Into<String>,
78    {
79        let required: HashSet<String> = required.into_iter().map(Into::into).collect();
80        let mut found: HashSet<String> = HashSet::new();
81
82        for export in self.module.exports() {
83            let name = export.name().to_string();
84            if required.contains(&name) {
85                found.insert(name);
86            }
87        }
88
89        let missing: Vec<_> = required.difference(&found).cloned().collect();
90        if !missing.is_empty() {
91            return Err(CardinalError::Other(format!(
92                "wasm plugin missing required exports: {:?}",
93                missing
94            )));
95        }
96
97        Ok(())
98    }
99}