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 {}",
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}