plux_lua_manager/
manager.rs

1//! Core plugin manager implementation for Lua plugins.
2//!
3//! This module provides the main [`LuaManager`] type which implements the `Manager`
4//! trait from the `plux-rs` crate, enabling the loading, management, and execution
5//! of Lua plugins in a safe and controlled environment.
6//!
7//! # Features
8//!
9//! - **Plugin Lifecycle Management**: Load, unload, and reload plugins at runtime
10//! - **Sandboxed Execution**: Each plugin runs in an isolated environment
11//! - **Dependency Injection**: Inject Rust functions and values into the Lua environment
12//! - **Error Handling**: Comprehensive error handling and reporting
13//! - **Thread Safety**: Safe concurrent execution of multiple plugins
14//!
15//! # Examples
16//!
17//! ```no_run
18//! ...
19//! 
20//! use plux_lua_manager::prelude::*;
21//! 
22//! ...
23//! 
24//! loader.context(move |mut ctx| {
25//!     ctx.register_manager(LuaManager::new()).unwrap();
26//! });
27//! 
28//! ...
29//! ```
30
31use 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
55/// The main manager type for Lua plugins.
56///
57/// This struct is responsible for managing the lifecycle of Lua plugins,
58/// including loading, unloading, and interacting with them. It maintains
59/// a mapping of plugin bundles to their respective Lua states.
60///
61/// # Thread Safety
62///
63/// `LuaManager` is `Send` and `Sync`, allowing it to be used safely across
64/// thread boundaries. Each plugin's Lua state is protected by a mutex to
65/// ensure thread safety.
66pub struct LuaManager {
67    /// Map of bundle identifiers to their Lua states
68    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    /// Creates a new instance of `LuaManager`.
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// use plux_lua_manager::LuaManager;
84    ///
85    /// let manager = LuaManager::new();
86    /// ```
87    pub fn new() -> Self {
88        Self {
89            lua_refs: HashMap::new(),
90        }
91    }
92
93    /// Loads and executes the plugin's source code.
94    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        // Set the package path to include the plugin's directory
110        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        // Execute the main script
126        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        // Register the plugin functions
131        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    /// Returns the format identifier for this manager ("lua").
170    fn format(&self) -> &'static str {
171        "lua"
172    }
173
174    /// Registers a new plugin.
175    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    /// Unregisters a plugin.
183    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    /// Loads a plugin into memory.
193    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        // Initialize the Lua environment
205        {
206            let lua_guard = lua.lock().unwrap();
207
208            vtable::register_vtable(&lua_guard, api.registry())?;
209
210            // Register the API
211            api::register_api(&lua_guard, &api)?;
212        }
213
214        // Load the plugin's source code
215        self.load_src(&lua, api.clone(), context.plugin().info().path.clone())?;
216
217        // Register any requested functions
218        let requests = requests::register_requests(&lua, context.requests())?;
219        for request in requests {
220            context.register_request(request)?;
221        }
222
223        // Store the Lua state
224        self.lua_refs.insert(bundle, lua);
225
226        Ok(())
227    }
228
229    /// Unloads a plugin from memory.
230    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        // Remove the Lua state
238        self.lua_refs.remove(bundle);
239
240        Ok(())
241    }
242}