cardinal_wasm_plugins/
plugin.rs1use 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 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 {e}"
57 )))
58 })?;
59
60 Ok((engine, module))
61 }
62
63 pub fn with_memory_name(mut self, name: String) -> Self {
64 self.memory_name = name;
65 self
66 }
67
68 pub fn with_handle_name(mut self, name: String) -> Self {
69 self.handle_name = name;
70 self
71 }
72
73 pub fn validate_exports<I, S>(&self, required: I) -> Result<(), CardinalError>
74 where
75 I: IntoIterator<Item = S>,
76 S: Into<String>,
77 {
78 let required: HashSet<String> = required.into_iter().map(Into::into).collect();
79 let mut found: HashSet<String> = HashSet::new();
80
81 for export in self.module.exports() {
82 let name = export.name().to_string();
83 if required.contains(&name) {
84 found.insert(name);
85 }
86 }
87
88 let missing: Vec<_> = required.difference(&found).cloned().collect();
89 if !missing.is_empty() {
90 return Err(CardinalError::Other(format!(
91 "wasm plugin missing required exports: {missing:?}"
92 )));
93 }
94
95 Ok(())
96 }
97}