1use std::collections::HashMap;
2use std::path::Path;
3use std::sync::Arc;
4
5use crate::error::PluginError;
6use crate::manifest::{PluginInfo, PluginKind};
7use crate::wasm::{WasmHost, WasmImports, WasmVal};
8use crate::wasm_actor::WasmActor;
9
10pub(crate) struct ActorTypeEntry<R: palladium_runtime::Reactor> {
14 pub(crate) plugin_name: String,
16 pub(crate) factory: ActorFactory<R>,
18}
19
20pub(crate) type ActorFactory<R> =
21 Box<dyn Fn(&[u8]) -> Result<Box<dyn palladium_actor::Actor<R>>, PluginError> + Send + Sync>;
22
23pub(crate) enum LoadedPlugin {
32 Native {
33 _lib: libloading::Library,
35 info: PluginInfo,
36 },
37 #[allow(dead_code)] Wasm { info: PluginInfo },
39}
40
41impl LoadedPlugin {
42 pub(crate) fn info(&self) -> &PluginInfo {
43 match self {
44 Self::Native { info, .. } => info,
45 Self::Wasm { info } => info,
46 }
47 }
48}
49
50pub struct PluginRegistry<R: palladium_runtime::Reactor> {
59 plugins: HashMap<String, LoadedPlugin>,
61 types: HashMap<String, ActorTypeEntry<R>>,
63}
64
65impl<R: palladium_runtime::Reactor> PluginRegistry<R> {
66 pub fn new() -> Self {
68 Self {
69 plugins: HashMap::new(),
70 types: HashMap::new(),
71 }
72 }
73
74 pub fn list(&self) -> Vec<PluginInfo> {
76 let mut infos: Vec<PluginInfo> = self.plugins.values().map(|p| p.info().clone()).collect();
77 infos.sort_by(|a, b| a.name.cmp(&b.name));
78 infos
79 }
80
81 pub fn create_actor(
89 &self,
90 type_name: &str,
91 config: &[u8],
92 ) -> Result<Box<dyn palladium_actor::Actor<R>>, PluginError> {
93 let entry = self
94 .types
95 .get(type_name)
96 .ok_or_else(|| PluginError::UnknownType(type_name.to_string()))?;
97 (entry.factory)(config)
98 }
99
100 pub fn actor_types_for_plugin(&self, plugin_name: &str) -> Vec<String> {
102 let mut names: Vec<String> = self
103 .types
104 .iter()
105 .filter(|(_, entry)| entry.plugin_name == plugin_name)
106 .map(|(type_name, _)| type_name.clone())
107 .collect();
108 names.sort();
109 names
110 }
111
112 pub fn unload(&mut self, name: &str) -> Result<(), PluginError> {
125 if !self.plugins.contains_key(name) {
126 return Err(PluginError::UnknownType(name.to_string()));
127 }
128 self.types.retain(|_, entry| entry.plugin_name != name);
129 self.plugins.remove(name);
130 Ok(())
131 }
132
133 pub fn register_type(
143 &mut self,
144 plugin_name: impl Into<String>,
145 type_name: impl Into<String>,
146 factory: impl Fn(&[u8]) -> Result<Box<dyn palladium_actor::Actor<R>>, PluginError>
147 + Send
148 + Sync
149 + 'static,
150 ) {
151 let plugin_name = plugin_name.into();
152 let type_name = type_name.into();
153 self.types.insert(
154 type_name,
155 ActorTypeEntry {
156 plugin_name,
157 factory: Box::new(factory),
158 },
159 );
160 }
161
162 pub(crate) fn insert_loaded(&mut self, plugin: LoadedPlugin) {
167 let name = plugin.info().name.clone();
168 self.plugins.insert(name, plugin);
169 }
170
171 pub fn load_wasm(
184 &mut self,
185 path: &Path,
186 host: Arc<dyn WasmHost>,
187 ) -> Result<PluginInfo, PluginError> {
188 let bytes = std::fs::read(path).map_err(|e| PluginError::LoadFailed(e.to_string()))?;
189
190 let module = host
191 .compile(&bytes)
192 .map_err(|e| PluginError::WasmError(e.to_string()))?;
193
194 let mut meta = host
196 .instantiate(module.as_ref(), WasmImports::default())
197 .map_err(|e| PluginError::WasmError(e.to_string()))?;
198
199 let init_rets = meta
200 .call("pd_plugin_init", &[])
201 .map_err(|e| PluginError::WasmError(e.to_string()))?;
202
203 let info_offset = match init_rets.first() {
204 Some(WasmVal::I32(off)) => *off as usize,
205 _ => return Err(PluginError::InitFailed),
206 };
207
208 let memory = meta
210 .memory_read(0, 65536)
211 .map_err(|e| PluginError::WasmError(e.to_string()))?;
212
213 let (info, type_names) = parse_wasm_plugin_info(&memory, info_offset)?;
214
215 let module = Arc::new(module);
217 for type_name in &type_names {
218 let mc = Arc::clone(&module);
219 let hc = Arc::clone(&host);
220 let tn = type_name.clone();
221 let pn = info.name.clone();
222 self.register_type(&pn, &tn, move |_config: &[u8]| {
223 Ok(Box::new(WasmActor::<R>::new(
224 Arc::clone(&mc),
225 Arc::clone(&hc),
226 1_000_000,
227 )) as Box<dyn palladium_actor::Actor<R>>)
228 });
229 }
230
231 self.insert_loaded(LoadedPlugin::Wasm { info: info.clone() });
232 Ok(info)
233 }
234}
235
236fn parse_wasm_plugin_info(
256 memory: &[u8],
257 offset: usize,
258) -> Result<(PluginInfo, Vec<String>), PluginError> {
259 const INFO_SIZE: usize = 32;
260 const TYPE_INFO_SIZE: usize = 16;
261
262 if offset + INFO_SIZE > memory.len() {
263 return Err(PluginError::InitFailed);
264 }
265
266 let name_ptr = read_u32_le(memory, offset)? as usize;
267 let name_len = read_u32_le(memory, offset + 4)? as usize;
268 let ver_major = read_u32_le(memory, offset + 8)?;
269 let ver_minor = read_u32_le(memory, offset + 12)?;
270 let ver_patch = read_u32_le(memory, offset + 16)?;
271 let abi_version = read_u32_le(memory, offset + 20)?;
272 let type_count = read_u32_le(memory, offset + 24)? as usize;
273 let types_ptr = read_u32_le(memory, offset + 28)? as usize;
274
275 if abi_version != palladium_plugin_api::PD_ABI_VERSION {
276 return Err(PluginError::AbiMismatch {
277 expected: palladium_plugin_api::PD_ABI_VERSION,
278 found: abi_version,
279 });
280 }
281
282 let name = read_str(memory, name_ptr, name_len)?;
283
284 let mut type_names = Vec::with_capacity(type_count);
285 for i in 0..type_count {
286 let base = types_ptr + i * TYPE_INFO_SIZE;
287 if base + TYPE_INFO_SIZE > memory.len() {
288 return Err(PluginError::InitFailed);
289 }
290 let tn_ptr = read_u32_le(memory, base)? as usize;
291 let tn_len = read_u32_le(memory, base + 4)? as usize;
292 type_names.push(read_str(memory, tn_ptr, tn_len)?);
293 }
294
295 let info = PluginInfo {
296 name,
297 version: format!("{ver_major}.{ver_minor}.{ver_patch}"),
298 kind: PluginKind::Wasm,
299 actor_type_count: type_count,
300 };
301
302 Ok((info, type_names))
303}
304
305fn read_u32_le(memory: &[u8], offset: usize) -> Result<u32, PluginError> {
306 let b = memory
307 .get(offset..offset + 4)
308 .ok_or(PluginError::InitFailed)?;
309 Ok(u32::from_le_bytes([b[0], b[1], b[2], b[3]]))
310}
311
312fn read_str(memory: &[u8], ptr: usize, len: usize) -> Result<String, PluginError> {
313 let bytes = memory.get(ptr..ptr + len).ok_or(PluginError::InitFailed)?;
314 std::str::from_utf8(bytes)
315 .map(|s| s.to_string())
316 .map_err(|_| PluginError::InitFailed)
317}
318
319impl<R: palladium_runtime::Reactor> Default for PluginRegistry<R> {
320 fn default() -> Self {
321 Self::new()
322 }
323}