diff --git a/plugin_test/src/lib.rs b/plugin_test/src/lib.rs index 2fe9488..8f09048 100644 --- a/plugin_test/src/lib.rs +++ b/plugin_test/src/lib.rs @@ -1,46 +1,52 @@ use async_trait::async_trait; use servers::{ - plugins::{ - Command, CommandManagerType, CommandRegistrar, Event, EventRegistrar, Plugin, - PluginRegistrar, - }, + plugins::{Command, Event, Plugin, PluginManagerType, Registrar}, tcp::Client, }; struct PluginTest; +/// Create a new plugin. #[async_trait] impl Plugin for PluginTest { + /// Name of the plugin. fn name(&self) -> &'static str { "test" } + /// A function will be executed when plugin loading. + /// Usally used for initialization. async fn on_plugin_load(&self) {} - - async fn on_plugin_unload(&self) {} } +/// Create a new command. #[async_trait] impl Command for PluginTest { + /// Command name fn name(&self) -> &'static str { "/test" } + /// Help message of the command fn help(&self) -> &'static str { "test command" } - async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &CommandManagerType) { + /// Command function + async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &PluginManagerType) { client.send("content").expect("send message") } } +/// Create a new event #[async_trait] impl Event for PluginTest { + /// Event name (onConnect or onSend) fn name(&self) -> &'static str { "onConnect" } + /// Event function async fn execute(&self, client: &mut Client) { client .send(&format!("Welcome {}", client.stream.peer_addr().unwrap())) @@ -48,16 +54,10 @@ impl Event for PluginTest { } } +/// Regsiter plugin #[no_mangle] -pub fn plugin_entry( - plugin: &mut dyn PluginRegistrar, - command: &mut dyn CommandRegistrar, - event: &mut dyn EventRegistrar, -) { - // register plugin - plugin.register(Box::new(PluginTest)); - // register command - command.register(Box::new(PluginTest)); - // register plugin - event.register(Box::new(PluginTest)); +pub fn plugin_entry(registrar: &mut dyn Registrar) { + registrar.register_plugin(Box::new(PluginTest)); + registrar.register_command(Box::new(PluginTest)); + registrar.register_event(Box::new(PluginTest)); } diff --git a/src/commands/help.rs b/src/commands/help.rs index 52abc33..9ebc0ff 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,7 +1,7 @@ use async_trait::async_trait; use crate::{ - plugins::{Command, CommandManagerType}, + plugins::{Command, PluginManagerType}, tcp::Client, }; @@ -21,9 +21,9 @@ impl Command for CommandHelp { &self, client: &mut Client, _args: Vec<&str>, - command_manager: &CommandManagerType, + plugin_manager: &PluginManagerType, ) { - for command in command_manager.commands.iter() { + for command in plugin_manager.commands.iter() { client .send(&format!("{} - {}", command.name(), command.help())) .expect("send message"); diff --git a/src/main.rs b/src/main.rs index 40394ae..9ad8c97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -53,19 +53,14 @@ async fn start_server(host: &str, port: &str) -> anyhow::Result<()> { println!("Tcp server started at: {}", listener.local_addr()?); // load plugins, commands and events - let (command_manager, _plugin_manager, event_manager) = loader()?; + let plugin_manager = loader()?; // Accepts a new incoming connection from this listener. while let Ok((stream, _address)) = listener.accept() { let client = Client::new(stream); - // clone `CommandManager` - let command_manager = command_manager.clone(); - // clone `EventManager` - let event_manager = event_manager.clone(); - // handle client connection in new thread - tokio::spawn(handle_connection(client, command_manager, event_manager)); + tokio::spawn(handle_connection(client, plugin_manager.clone())); } // server for a unexpectedly reason be terminated diff --git a/src/plugins/loader.rs b/src/plugins/loader.rs index 28c7aee..96e1e40 100644 --- a/src/plugins/loader.rs +++ b/src/plugins/loader.rs @@ -1,174 +1,24 @@ -use std::{any::Any, fs, sync::Arc}; +use std::{fs, sync::Arc}; -use async_trait::async_trait; use libloading::{Library, Symbol}; use log::{debug, trace}; -use crate::{commands, tcp::Client}; +use crate::{commands, plugins::Registrar}; -/// A plugin which allows you to add extra functionality. -#[async_trait] -pub trait Plugin: Any + Send + Sync { - /// Get a name describing the `Plugin`. - fn name(&self) -> &'static str; - /// A function that runs immediately after plugin loading. - /// Usually used for initialization. - async fn on_plugin_load(&self); - /// A function that runs immediately before the plugin is unloaded. - /// Use this if you want to do any cleanup. - async fn on_plugin_unload(&self); -} - -/// Trait with function to register plugin. -pub trait PluginRegistrar { - /// Function to register the plugin - fn register(&mut self, plugin: Box); -} - -impl PluginRegistrar for PluginManager { - fn register(&mut self, plugin: Box) { - self.plugins.push(plugin) - } -} - -/// Plugin Manager -pub struct PluginManager { - /// Vector with all loaded plugins. - pub plugins: Vec>, -} - -impl PluginManager { - /// Create empty `PluginManager`. - pub fn new() -> Self { - Self { - plugins: Vec::new(), - } - } -} - -impl Default for PluginManager { - fn default() -> Self { - Self::new() - } -} - -/// Plugin Manager Type -pub type PluginManagerType = Arc; - -/// Trait with command functions to implement on struct. -#[async_trait] -pub trait Command: Any + Send + Sync { - /// Command name - fn name(&self) -> &'static str; - /// Help message of this command - fn help(&self) -> &'static str; - /// Command function - async fn execute( - &self, - client: &mut Client, - args: Vec<&str>, - command_manager: &CommandManagerType, - ); -} - -/// Command Manager -pub struct CommandManager { - /// Vector with all commands. - pub commands: Vec>, -} - -impl CommandManager { - /// Create empty `CommandManager`. - pub fn new() -> Self { - Self { - commands: Vec::new(), - } - } -} - -impl Default for CommandManager { - fn default() -> Self { - Self::new() - } -} - -/// Command Manager Type -pub type CommandManagerType = Arc; - -/// Trait with function to register command. -pub trait CommandRegistrar { - /// Function to register the plugin and the commands in the plugin. - fn register(&mut self, command: Box); -} - -impl CommandRegistrar for CommandManager { - fn register(&mut self, command: Box) { - self.commands.push(command) - } -} - -/// Trait with event functions to implement on struct. -#[async_trait] -pub trait Event: Any + Send + Sync { - /// Event name (onConnect, onSend) - fn name(&self) -> &'static str; - /// Event function - async fn execute(&self, client: &mut Client); -} - -/// Event Manager -pub struct EventManager { - /// Vector with all events loaded from plugins. - pub events: Vec>, -} - -impl EventManager { - /// Create empty `EventManager` - pub fn new() -> Self { - Self { events: Vec::new() } - } -} - -impl Default for EventManager { - fn default() -> Self { - Self::new() - } -} - - -/// Event Manager Type -pub type EventManagerType = Arc; - -/// Trait with function to register event. -pub trait EventRegistrar { - /// Function to register the plugin and the commands in the plugin. - fn register(&mut self, command: Box); -} - -impl EventRegistrar for EventManager { - fn register(&mut self, command: Box) { - self.events.push(command) - } -} +use super::{PluginManager, PluginManagerType}; /// Plugins and Commands loader -pub fn loader() -> anyhow::Result<(CommandManagerType, PluginManagerType, EventManagerType)> { +pub fn loader() -> anyhow::Result { // get path to .so lib from command argument let config_dir = "./plugins"; let paths = fs::read_dir(config_dir)?; - // create a plugin manager where all loaded plugins will be located + // create a plugin manager let mut plugin_manager = PluginManager::new(); - // create a command manager where located all commands - let mut command_manager = CommandManager::new(); - - // create a command manager where located all events from plugins - let mut event_manager = EventManager::new(); - // register default commands for command in commands::register_commands() { - command_manager.commands.push(command) + plugin_manager.commands.push(command) } // for all plugin in directory @@ -189,28 +39,15 @@ pub fn loader() -> anyhow::Result<(CommandManagerType, PluginManagerType, EventM // get `plugin_entry` from library trace!("Finding symbol `plugin_entry` in `{}`", plugin_path); - let func: Symbol< - unsafe extern "C" fn( - &mut dyn PluginRegistrar, - &mut dyn CommandRegistrar, - &mut dyn EventRegistrar, - ) -> (), - > = lib.get(b"plugin_entry")?; + let func: Symbol ()> = + lib.get(b"plugin_entry")?; // execute initial plugin function trace!("Running `plugin_entry(...)` in plugin `{}`", plugin_path); - func( - &mut plugin_manager, - &mut command_manager, - &mut event_manager, - ); + func(&mut plugin_manager); } } - // return CommandManager, PluginManager and EventManager - Ok(( - Arc::new(command_manager), - Arc::new(plugin_manager), - Arc::new(event_manager), - )) + // return a `PluginManager` + Ok(Arc::new(plugin_manager)) } diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 4fa8ee8..28398cc 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -23,18 +23,16 @@ //! //! In file `src/lib.rs` //! -//! ``` +//! ```no_run //! use async_trait::async_trait; //! use servers::{ -//! plugins::{ -//! Command, CommandManagerType, CommandRegistrar, EventRegistrar, Plugin, -//! PluginRegistrar, -//! }, +//! plugins::{Command, Plugin, PluginManagerType, Registrar}, //! tcp::Client, //! }; //! //! struct PluginTest; //! +//! /// Create a new plugin. //! #[async_trait] //! impl Plugin for PluginTest { //! /// Name of the plugin. @@ -42,17 +40,12 @@ //! "test" //! } //! -//! /// Function will be executed when plugin loading. -//! async fn on_plugin_load(&self) { -//! println!("Loading plugin `test`...") -//! } -//! -//! /// Function will be executed when plugin unloading. -//! async fn on_plugin_unload(&self) { -//! println!("Unloading plugin `test`...") -//! } +//! /// A function will be executed when plugin loading. +//! /// Usally used for initialization. +//! async fn on_plugin_load(&self) {} //! } //! +//! /// Create a new command. //! #[async_trait] //! impl Command for PluginTest { //! /// Command name @@ -60,28 +53,22 @@ //! "/test" //! } //! -//! /// Command help message +//! /// Help message of the command //! fn help(&self) -> &'static str { //! "test command" //! } //! -//! /// Function will be executed when client send command `/test` -//! async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &CommandManagerType) { -//! client.send("Message sended by `test` plugin").expect("send message") +//! /// Command function +//! async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &PluginManagerType) { +//! client.send("content").expect("send message") //! } //! } //! -//! /// Register plugin and event +//! /// Regsiter plugin //! #[no_mangle] -//! pub fn plugin_entry( -//! plugin: &mut dyn PluginRegistrar, -//! command: &mut dyn CommandRegistrar, -//! _event: &mut dyn EventRegistrar, -//! ) { -//! // register plugin -//! plugin.register(Box::new(PluginTest)); -//! // register command -//! command.register(Box::new(PluginTest)); +//! pub fn plugin_entry(registrar: &mut dyn Registrar) { +//! registrar.register_plugin(Box::new(PluginTest)); +//! registrar.register_command(Box::new(PluginTest)); //! } //! ``` //! @@ -89,18 +76,16 @@ //! //! In file `src/lib.rs` //! -//! ``` +//! ```no_run //! use async_trait::async_trait; //! use servers::{ -//! plugins::{ -//! CommandManagerType, CommandRegistrar, Event, EventRegistrar, Plugin, -//! PluginRegistrar, -//! }, +//! plugins::{Event, Plugin, PluginManagerType, Registrar}, //! tcp::Client, //! }; //! //! struct PluginTest; //! +//! /// Create a new plugin. //! #[async_trait] //! impl Plugin for PluginTest { //! /// Name of the plugin. @@ -108,43 +93,32 @@ //! "test" //! } //! -//! /// Function will be executed when plugin loading. -//! async fn on_plugin_load(&self) { -//! println!("Loading plugin `test`...") -//! } -//! -//! /// Function will be executed when plugin unloading. -//! async fn on_plugin_unload(&self) { -//! println!("Unloading plugin `test`...") -//! } +//! /// A function will be executed when plugin loading. +//! /// Usally used for initialization. +//! async fn on_plugin_load(&self) {} //! } //! +//! /// Create a new event //! #[async_trait] //! impl Event for PluginTest { -//! /// Event name (onConnect, onSend) +//! /// Event name (onConnect or onSend) //! fn name(&self) -> &'static str { //! "onConnect" //! } //! -//! /// Function will be executed when client connected -//! async fn execute(&self, client: &mut Client) { -//! client -//! .send(&format!("Welcome {}", client.stream.peer_addr().unwrap())) -//! .expect("send message") +//! /// Event function +//! async fn execute(&self, client: &mut Client) { +//! client +//! .send(&format!("Welcome {}", client.stream.peer_addr().unwrap())) +//! .expect("send message") //! } //! } //! -//! /// Register plugin and command +//! /// Regsiter plugin //! #[no_mangle] -//! pub fn plugin_entry( -//! plugin: &mut dyn PluginRegistrar, -//! _command: &mut dyn CommandRegistrar, -//! event: &mut dyn EventRegistrar, -//! ) { -//! // register plugin -//! plugin.register(Box::new(PluginTest)); -//! // register event -//! event.register(Box::new(PluginTest)); +//! pub fn plugin_entry(registrar: &mut dyn Registrar) { +//! registrar.register_plugin(Box::new(PluginTest)); +//! registrar.register_event(Box::new(PluginTest)); //! } //! ``` //! @@ -157,5 +131,7 @@ //! Move compiled plugin to the `plugin` directory where servers is located mod loader; +mod types; pub use loader::*; +pub use types::*; diff --git a/src/plugins/types.rs b/src/plugins/types.rs new file mode 100644 index 0000000..92f9493 --- /dev/null +++ b/src/plugins/types.rs @@ -0,0 +1,94 @@ +use std::{any::Any, sync::Arc}; + +use async_trait::async_trait; + +use crate::tcp::Client; + +/// A plugin wich allows you to add extra functionality. +#[async_trait] +pub trait Plugin: Any + Send + Sync { + /// Name of the plugin. + fn name(&self) -> &'static str; + /// A function will be executed when plugin loading. + /// Usally used for initialization. + async fn on_plugin_load(&self); +} + +/// Add a new command +#[async_trait] +pub trait Command: Any + Send + Sync { + /// Name of the command. + fn name(&self) -> &'static str; + /// Help message of the command. + fn help(&self) -> &'static str; + /// Command function + async fn execute( + &self, + client: &mut Client, + args: Vec<&str>, + plugin_manager: &PluginManagerType, + ); +} + +/// Add a new function that will be executed when the event occurs +#[async_trait] +pub trait Event: Any + Send + Sync { + /// Event name (onConnect or onSend) + fn name(&self) -> &'static str; + /// Event function + async fn execute(&self, client: &mut Client); +} + +/// Plugin Manager +pub struct PluginManager { + /// Vector with loaded plugins. + pub plugins: Vec>, + /// Vector with all commands. + pub commands: Vec>, + /// Vector with all events. + pub events: Vec>, +} + +impl PluginManager { + /// Create an empty [PluginManager] + pub fn new() -> Self { + Self { + plugins: Vec::new(), + commands: Vec::new(), + events: Vec::new(), + } + } +} + +impl Default for PluginManager { + fn default() -> Self { + Self::new() + } +} + +/// Type of the [PluginManager] +pub type PluginManagerType = Arc; + +/// Plugin Registrar +pub trait Registrar { + /// Function to register the plugin + fn register_plugin(&mut self, plugin: Box); + /// Function to register the command + fn register_command(&mut self, command: Box); + /// Function to register the event + fn register_event(&mut self, event: Box); +} + +impl Registrar for PluginManager { + fn register_plugin(&mut self, plugin: Box) { + self.plugins.push(plugin) + } + + fn register_command(&mut self, command: Box) { + self.commands.push(command) + } + + fn register_event(&mut self, event: Box) { + self.events.push(event) + } +} diff --git a/src/tcp/handle_connection.rs b/src/tcp/handle_connection.rs index b97240a..089b7d7 100644 --- a/src/tcp/handle_connection.rs +++ b/src/tcp/handle_connection.rs @@ -2,27 +2,26 @@ use std::io::Write; use log::trace; -use crate::plugins::{CommandManagerType, EventManagerType}; +use crate::plugins::PluginManagerType; use super::Client; /// Handle Client connection pub async fn handle_connection( mut client: Client, - commands: CommandManagerType, - events: EventManagerType, + plugin_manager: PluginManagerType, ) -> anyhow::Result<()> { println!("New Client: {:?}", client.stream.peer_addr()?); // run `onConnect` events from plugins - check_event(&mut client, &events, "onConnect").await; + check_event(&mut client, &plugin_manager, "onConnect").await; loop { // read client message/buffer let buf = client.read()?; // run `onSend` events from plugins - check_event(&mut client, &events, "onSend").await; + check_event(&mut client, &plugin_manager, "onSend").await; // split message by whitespaces let args: Vec<&str> = buf.split_ascii_whitespace().collect(); @@ -32,21 +31,21 @@ pub async fn handle_connection( client.send("empty buffer").expect("send message"); // don't execute the following commands because it causes panic - continue + continue; } // get command from args let cmd = args[0]; // search if a command exists - for command in commands.commands.iter() { + for command in plugin_manager.commands.iter() { // if this is the entered command if cmd == command.name() { trace!("Executing a command `{}`", command.name()); // execute command command - .execute(&mut client, args[1..args.len()].to_vec(), &commands) + .execute(&mut client, args[1..args.len()].to_vec(), &plugin_manager) .await; // don't search for more commands @@ -65,7 +64,7 @@ pub async fn handle_connection( } /// Search for a events and execute it -async fn check_event(client: &mut Client, events: &EventManagerType, event_name: &str) { +async fn check_event(client: &mut Client, events: &PluginManagerType, event_name: &str) { for event in events.events.iter() { // check if this event should be started if event.name() == event_name {