plux_lua_manager/
manager.rs1use std::{
32 path::PathBuf,
33 sync::{Arc, Mutex},
34};
35
36use hashbrown::HashMap;
37use log;
38use mlua::{Function, Lua, MultiValue, Table, Value};
39use plux_rs::{
40 Api, Bundle, Manager, Plugin, StdInfo,
41 context::LoadPluginContext,
42 function::{Arg, DynamicFunction, FunctionOutput},
43 utils::ManagerResult,
44 variable::VariableType,
45};
46
47use crate::error::{ManagerError, PluginError};
48use crate::{
49 config::load_config,
50 lua::{api, requests, vtable},
51};
52
53use crate::lua::conversion::{lua_to_plux, plux_to_lua};
54
55pub struct LuaManager {
67 lua_refs: HashMap<Bundle, Arc<Mutex<Lua>>>,
69}
70
71impl Default for LuaManager {
72 fn default() -> Self {
73 Self::new()
74 }
75}
76
77impl LuaManager {
78 pub fn new() -> Self {
88 Self {
89 lua_refs: HashMap::new(),
90 }
91 }
92
93 fn load_src(
95 &self,
96 lua: &Arc<Mutex<Lua>>,
97 api: Arc<Api<FunctionOutput, StdInfo>>,
98 path: PathBuf,
99 ) -> Result<(), ManagerError> {
100 let main_path = path.join("main.lua");
101 if !main_path.exists() {
102 return Err(ManagerError::Plugin(PluginError::SourceError(
103 "main.lua not found".to_string(),
104 )));
105 }
106
107 let lua_guard = lua.lock().unwrap();
108
109 let package_path = format!(
111 "{}/?.lua;{}/?/init.lua",
112 path.to_string_lossy(),
113 path.to_string_lossy()
114 );
115
116 lua_guard
117 .load(&format!(
118 r#"
119 package.path = '{};' .. package.path
120 "#,
121 package_path
122 ))
123 .exec()?;
124
125 let src = std::fs::read_to_string(main_path)
127 .map_err(|e| ManagerError::Plugin(PluginError::IoError(e)))?;
128 let result: Vec<Table> = lua_guard.load(&src).eval()?;
129
130 let plugin = api.get_plugin_mut_by_bundle(api.plugin()).unwrap();
132 for info in result.into_iter() {
133 let name: String = info.get("name")?;
134 let inputs: Vec<String> = info.get("inputs")?;
135 let lua_function: Function = info.get("func")?;
136
137 let lua_clone = lua.clone();
138 let function = DynamicFunction::new(
139 name.clone(),
140 inputs
141 .iter()
142 .map(|name| Arg::new(name, VariableType::Let))
143 .collect(),
144 Some(Arg::new("output", VariableType::Let)),
145 move |args| {
146 let mut lua_args = vec![];
147 for arg in args {
148 lua_args.push(plux_to_lua(arg, &*lua_clone.lock().unwrap())?);
149 }
150
151 let result = match lua_function.call::<Value>(MultiValue::from_vec(lua_args))? {
152 Value::Nil => Ok(None),
153 value => Ok(Some(lua_to_plux(&value)?)),
154 };
155 result
156 },
157 );
158
159 plugin
160 .register_function(function)
161 .map_err(|e| ManagerError::Plugin(PluginError::RegisterFunctionError(e)))?;
162 }
163
164 Ok(())
165 }
166}
167
168impl<'a> Manager<'a, FunctionOutput, StdInfo> for LuaManager {
169 fn format(&self) -> &'static str {
171 "lua"
172 }
173
174 fn register_plugin(&mut self, context: plux_rs::RegisterPluginContext) -> ManagerResult<StdInfo> {
176 let (_, info) = load_config(&context.path).map_err(|e| ManagerError::Config(e))?;
177
178 log::info!("Registering plugin: {}", context.bundle);
179 Ok(info)
180 }
181
182 fn unregister_plugin(
184 &mut self,
185 plugin: &Plugin<'a, FunctionOutput, StdInfo>,
186 ) -> ManagerResult<()> {
187 let bundle = &plugin.info().bundle;
188 log::info!("Unregistering plugin: {}", bundle);
189 Ok(())
190 }
191
192 fn load_plugin(
194 &mut self,
195 mut context: LoadPluginContext<'a, '_, FunctionOutput, StdInfo>,
196 api: Api<FunctionOutput, StdInfo>,
197 ) -> ManagerResult<()> {
198 let bundle = context.plugin().info().bundle.clone();
199 log::info!("Loading plugin: {}", bundle);
200
201 let lua = Arc::new(Mutex::new(Lua::new()));
202 let api = Arc::new(api);
203
204 {
206 let lua_guard = lua.lock().unwrap();
207
208 vtable::register_vtable(&lua_guard, api.registry())?;
209
210 api::register_api(&lua_guard, &api)?;
212 }
213
214 self.load_src(&lua, api.clone(), context.plugin().info().path.clone())?;
216
217 let requests = requests::register_requests(&lua, context.requests())?;
219 for request in requests {
220 context.register_request(request)?;
221 }
222
223 self.lua_refs.insert(bundle, lua);
225
226 Ok(())
227 }
228
229 fn unload_plugin(
231 &mut self,
232 plugin: &Plugin<'a, FunctionOutput, StdInfo>,
233 ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
234 let bundle = &plugin.info().bundle;
235 log::info!("Unloading plugin: {}", bundle);
236
237 self.lua_refs.remove(bundle);
239
240 Ok(())
241 }
242}