palladium_plugin/
plugin_rpc.rs1use serde_json::{json, Value};
8use std::sync::{Arc, Mutex};
9
10use palladium_runtime::PluginRpcHandler;
11
12use crate::manifest::PluginKind;
13use crate::registry::PluginRegistry;
14use crate::wasm::WasmHost;
15
16pub struct RegistryRpcHandler<R: palladium_runtime::Reactor> {
22 registry: Arc<Mutex<PluginRegistry<R>>>,
23 paths: Arc<Mutex<std::collections::HashMap<String, std::path::PathBuf>>>,
25 wasm_host: Option<Arc<dyn WasmHost>>,
27}
28
29impl<R: palladium_runtime::Reactor> RegistryRpcHandler<R> {
30 pub fn new() -> Self {
32 Self {
33 registry: Arc::new(Mutex::new(PluginRegistry::new())),
34 paths: Arc::new(Mutex::new(std::collections::HashMap::new())),
35 wasm_host: None,
36 }
37 }
38
39 pub fn with_registry(registry: Arc<Mutex<PluginRegistry<R>>>) -> Self {
41 Self {
42 registry,
43 paths: Arc::new(Mutex::new(std::collections::HashMap::new())),
44 wasm_host: None,
45 }
46 }
47
48 pub fn with_wasm_host(mut self, host: Arc<dyn WasmHost>) -> Self {
50 self.wasm_host = Some(host);
51 self
52 }
53
54 pub fn registry(&self) -> Arc<Mutex<PluginRegistry<R>>> {
56 Arc::clone(&self.registry)
57 }
58}
59
60impl<R: palladium_runtime::Reactor> Default for RegistryRpcHandler<R> {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl<R: palladium_runtime::Reactor> PluginRpcHandler<R> for RegistryRpcHandler<R> {
67 fn list_plugins(&self) -> Vec<Value> {
68 let reg = self.registry.lock().unwrap();
69 let infos = reg.list();
70 let paths = self.paths.lock().unwrap().clone();
71 infos
72 .into_iter()
73 .map(|info| {
74 let actor_types = reg.actor_types_for_plugin(&info.name);
75 let path = paths
76 .get(&info.name)
77 .map(|p| p.to_string_lossy().into_owned());
78 json!({
79 "name": info.name,
80 "version": info.version,
81 "kind": match info.kind {
82 PluginKind::Native => "native",
83 PluginKind::Wasm => "wasm",
84 },
85 "actor_type_count": info.actor_type_count,
86 "actor_types": actor_types,
87 "path": path,
88 })
89 })
90 .collect()
91 }
92
93 fn load_plugin(&self, path_str: &str) -> Result<Value, String> {
94 let path = std::path::Path::new(path_str);
95 let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
96
97 let (info, actor_types) = {
98 let mut reg = self.registry.lock().unwrap();
99 let info = if ext == "wasm" {
100 let host = self
101 .wasm_host
102 .as_ref()
103 .ok_or_else(|| "WASM host not configured".to_string())?;
104 reg.load_wasm(path, Arc::clone(host))
105 .map_err(|e: crate::error::PluginError| e.to_string())?
106 } else {
107 reg.load_native(path)
108 .map_err(|e: crate::error::PluginError| e.to_string())?
109 };
110 let actor_types = reg.actor_types_for_plugin(&info.name);
111 (info, actor_types)
112 };
113
114 self.paths
116 .lock()
117 .unwrap()
118 .insert(info.name.clone(), path.to_path_buf());
119
120 Ok(json!({
121 "name": info.name,
122 "version": info.version,
123 "kind": match info.kind {
124 PluginKind::Native => "native",
125 PluginKind::Wasm => "wasm",
126 },
127 "actor_type_count": info.actor_type_count,
128 "actor_types": actor_types,
129 "path": path.to_string_lossy().to_string(),
130 }))
131 }
132
133 fn unload_plugin(&self, name: &str) -> Result<(), String> {
134 self.registry
135 .lock()
136 .unwrap()
137 .unload(name)
138 .map_err(|e: crate::error::PluginError| e.to_string())?;
139 self.paths.lock().unwrap().remove(name);
140 Ok(())
141 }
142
143 fn reload_plugin(&self, name: &str) -> Result<Value, String> {
144 let path = self
145 .paths
146 .lock()
147 .unwrap()
148 .get(name)
149 .cloned()
150 .ok_or_else(|| format!("No recorded path for plugin '{name}' — load it first"))?;
151
152 let _ = self.registry.lock().unwrap().unload(name);
154
155 self.load_plugin(path.to_str().unwrap_or(""))
157 }
158
159 fn spawn_actor(
160 &self,
161 type_name: &str,
162 config: &[u8],
163 ) -> Result<Box<dyn palladium_actor::Actor<R>>, String> {
164 let reg = self.registry.lock().unwrap();
165 reg.create_actor(type_name, config)
166 .map_err(|e: crate::error::PluginError| e.to_string())
167 }
168}