feat(plugins-loader): improve a plugin loader

This commit is contained in:
MedzikUser 2022-06-20 23:22:45 +02:00
parent 369dd14cf2
commit 4f9baa4b76
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
7 changed files with 170 additions and 269 deletions

View File

@ -1,46 +1,52 @@
use async_trait::async_trait; use async_trait::async_trait;
use servers::{ use servers::{
plugins::{ plugins::{Command, Event, Plugin, PluginManagerType, Registrar},
Command, CommandManagerType, CommandRegistrar, Event, EventRegistrar, Plugin,
PluginRegistrar,
},
tcp::Client, tcp::Client,
}; };
struct PluginTest; struct PluginTest;
/// Create a new plugin.
#[async_trait] #[async_trait]
impl Plugin for PluginTest { impl Plugin for PluginTest {
/// Name of the plugin.
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"test" "test"
} }
/// A function will be executed when plugin loading.
/// Usally used for initialization.
async fn on_plugin_load(&self) {} async fn on_plugin_load(&self) {}
async fn on_plugin_unload(&self) {}
} }
/// Create a new command.
#[async_trait] #[async_trait]
impl Command for PluginTest { impl Command for PluginTest {
/// Command name
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"/test" "/test"
} }
/// Help message of the command
fn help(&self) -> &'static str { fn help(&self) -> &'static str {
"test command" "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") client.send("content").expect("send message")
} }
} }
/// Create a new event
#[async_trait] #[async_trait]
impl Event for PluginTest { impl Event for PluginTest {
/// Event name (onConnect or onSend)
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"onConnect" "onConnect"
} }
/// Event function
async fn execute(&self, client: &mut Client) { async fn execute(&self, client: &mut Client) {
client client
.send(&format!("Welcome {}", client.stream.peer_addr().unwrap())) .send(&format!("Welcome {}", client.stream.peer_addr().unwrap()))
@ -48,16 +54,10 @@ impl Event for PluginTest {
} }
} }
/// Regsiter plugin
#[no_mangle] #[no_mangle]
pub fn plugin_entry( pub fn plugin_entry(registrar: &mut dyn Registrar) {
plugin: &mut dyn PluginRegistrar, registrar.register_plugin(Box::new(PluginTest));
command: &mut dyn CommandRegistrar, registrar.register_command(Box::new(PluginTest));
event: &mut dyn EventRegistrar, registrar.register_event(Box::new(PluginTest));
) {
// register plugin
plugin.register(Box::new(PluginTest));
// register command
command.register(Box::new(PluginTest));
// register plugin
event.register(Box::new(PluginTest));
} }

View File

@ -1,7 +1,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{ use crate::{
plugins::{Command, CommandManagerType}, plugins::{Command, PluginManagerType},
tcp::Client, tcp::Client,
}; };
@ -21,9 +21,9 @@ impl Command for CommandHelp {
&self, &self,
client: &mut Client, client: &mut Client,
_args: Vec<&str>, _args: Vec<&str>,
command_manager: &CommandManagerType, plugin_manager: &PluginManagerType,
) { ) {
for command in command_manager.commands.iter() { for command in plugin_manager.commands.iter() {
client client
.send(&format!("{} - {}", command.name(), command.help())) .send(&format!("{} - {}", command.name(), command.help()))
.expect("send message"); .expect("send message");

View File

@ -53,19 +53,14 @@ async fn start_server(host: &str, port: &str) -> anyhow::Result<()> {
println!("Tcp server started at: {}", listener.local_addr()?); println!("Tcp server started at: {}", listener.local_addr()?);
// load plugins, commands and events // 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. // Accepts a new incoming connection from this listener.
while let Ok((stream, _address)) = listener.accept() { while let Ok((stream, _address)) = listener.accept() {
let client = Client::new(stream); 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 // 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 // server for a unexpectedly reason be terminated

View File

@ -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 libloading::{Library, Symbol};
use log::{debug, trace}; use log::{debug, trace};
use crate::{commands, tcp::Client}; use crate::{commands, plugins::Registrar};
/// A plugin which allows you to add extra functionality. use super::{PluginManager, PluginManagerType};
#[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<dyn Plugin>);
}
impl PluginRegistrar for PluginManager {
fn register(&mut self, plugin: Box<dyn Plugin>) {
self.plugins.push(plugin)
}
}
/// Plugin Manager
pub struct PluginManager {
/// Vector with all loaded plugins.
pub plugins: Vec<Box<dyn Plugin>>,
}
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<PluginManager>;
/// 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<Box<dyn Command>>,
}
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<CommandManager>;
/// 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<dyn Command>);
}
impl CommandRegistrar for CommandManager {
fn register(&mut self, command: Box<dyn Command>) {
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<Box<dyn Event>>,
}
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<EventManager>;
/// 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<dyn Event>);
}
impl EventRegistrar for EventManager {
fn register(&mut self, command: Box<dyn Event>) {
self.events.push(command)
}
}
/// Plugins and Commands loader /// Plugins and Commands loader
pub fn loader() -> anyhow::Result<(CommandManagerType, PluginManagerType, EventManagerType)> { pub fn loader() -> anyhow::Result<PluginManagerType> {
// get path to .so lib from command argument // get path to .so lib from command argument
let config_dir = "./plugins"; let config_dir = "./plugins";
let paths = fs::read_dir(config_dir)?; 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(); 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 // register default commands
for command in commands::register_commands() { for command in commands::register_commands() {
command_manager.commands.push(command) plugin_manager.commands.push(command)
} }
// for all plugin in directory // for all plugin in directory
@ -189,28 +39,15 @@ pub fn loader() -> anyhow::Result<(CommandManagerType, PluginManagerType, EventM
// get `plugin_entry` from library // get `plugin_entry` from library
trace!("Finding symbol `plugin_entry` in `{}`", plugin_path); trace!("Finding symbol `plugin_entry` in `{}`", plugin_path);
let func: Symbol< let func: Symbol<unsafe extern "C" fn(&mut dyn Registrar) -> ()> =
unsafe extern "C" fn( lib.get(b"plugin_entry")?;
&mut dyn PluginRegistrar,
&mut dyn CommandRegistrar,
&mut dyn EventRegistrar,
) -> (),
> = lib.get(b"plugin_entry")?;
// execute initial plugin function // execute initial plugin function
trace!("Running `plugin_entry(...)` in plugin `{}`", plugin_path); trace!("Running `plugin_entry(...)` in plugin `{}`", plugin_path);
func( func(&mut plugin_manager);
&mut plugin_manager,
&mut command_manager,
&mut event_manager,
);
} }
} }
// return CommandManager, PluginManager and EventManager // return a `PluginManager`
Ok(( Ok(Arc::new(plugin_manager))
Arc::new(command_manager),
Arc::new(plugin_manager),
Arc::new(event_manager),
))
} }

View File

@ -23,18 +23,16 @@
//! //!
//! In file `src/lib.rs` //! In file `src/lib.rs`
//! //!
//! ``` //! ```no_run
//! use async_trait::async_trait; //! use async_trait::async_trait;
//! use servers::{ //! use servers::{
//! plugins::{ //! plugins::{Command, Plugin, PluginManagerType, Registrar},
//! Command, CommandManagerType, CommandRegistrar, EventRegistrar, Plugin,
//! PluginRegistrar,
//! },
//! tcp::Client, //! tcp::Client,
//! }; //! };
//! //!
//! struct PluginTest; //! struct PluginTest;
//! //!
//! /// Create a new plugin.
//! #[async_trait] //! #[async_trait]
//! impl Plugin for PluginTest { //! impl Plugin for PluginTest {
//! /// Name of the plugin. //! /// Name of the plugin.
@ -42,17 +40,12 @@
//! "test" //! "test"
//! } //! }
//! //!
//! /// Function will be executed when plugin loading. //! /// A function will be executed when plugin loading.
//! async fn on_plugin_load(&self) { //! /// Usally used for initialization.
//! println!("Loading plugin `test`...") //! async fn on_plugin_load(&self) {}
//! }
//!
//! /// Function will be executed when plugin unloading.
//! async fn on_plugin_unload(&self) {
//! println!("Unloading plugin `test`...")
//! }
//! } //! }
//! //!
//! /// Create a new command.
//! #[async_trait] //! #[async_trait]
//! impl Command for PluginTest { //! impl Command for PluginTest {
//! /// Command name //! /// Command name
@ -60,28 +53,22 @@
//! "/test" //! "/test"
//! } //! }
//! //!
//! /// Command help message //! /// Help message of the command
//! fn help(&self) -> &'static str { //! fn help(&self) -> &'static str {
//! "test command" //! "test command"
//! } //! }
//! //!
//! /// Function will be executed when client send command `/test` //! /// Command function
//! async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &CommandManagerType) { //! async fn execute(&self, client: &mut Client, _args: Vec<&str>, _commands: &PluginManagerType) {
//! client.send("Message sended by `test` plugin").expect("send message") //! client.send("content").expect("send message")
//! } //! }
//! } //! }
//! //!
//! /// Register plugin and event //! /// Regsiter plugin
//! #[no_mangle] //! #[no_mangle]
//! pub fn plugin_entry( //! pub fn plugin_entry(registrar: &mut dyn Registrar) {
//! plugin: &mut dyn PluginRegistrar, //! registrar.register_plugin(Box::new(PluginTest));
//! command: &mut dyn CommandRegistrar, //! registrar.register_command(Box::new(PluginTest));
//! _event: &mut dyn EventRegistrar,
//! ) {
//! // register plugin
//! plugin.register(Box::new(PluginTest));
//! // register command
//! command.register(Box::new(PluginTest));
//! } //! }
//! ``` //! ```
//! //!
@ -89,18 +76,16 @@
//! //!
//! In file `src/lib.rs` //! In file `src/lib.rs`
//! //!
//! ``` //! ```no_run
//! use async_trait::async_trait; //! use async_trait::async_trait;
//! use servers::{ //! use servers::{
//! plugins::{ //! plugins::{Event, Plugin, PluginManagerType, Registrar},
//! CommandManagerType, CommandRegistrar, Event, EventRegistrar, Plugin,
//! PluginRegistrar,
//! },
//! tcp::Client, //! tcp::Client,
//! }; //! };
//! //!
//! struct PluginTest; //! struct PluginTest;
//! //!
//! /// Create a new plugin.
//! #[async_trait] //! #[async_trait]
//! impl Plugin for PluginTest { //! impl Plugin for PluginTest {
//! /// Name of the plugin. //! /// Name of the plugin.
@ -108,25 +93,20 @@
//! "test" //! "test"
//! } //! }
//! //!
//! /// Function will be executed when plugin loading. //! /// A function will be executed when plugin loading.
//! async fn on_plugin_load(&self) { //! /// Usally used for initialization.
//! println!("Loading plugin `test`...") //! async fn on_plugin_load(&self) {}
//! }
//!
//! /// Function will be executed when plugin unloading.
//! async fn on_plugin_unload(&self) {
//! println!("Unloading plugin `test`...")
//! }
//! } //! }
//! //!
//! /// Create a new event
//! #[async_trait] //! #[async_trait]
//! impl Event for PluginTest { //! impl Event for PluginTest {
//! /// Event name (onConnect, onSend) //! /// Event name (onConnect or onSend)
//! fn name(&self) -> &'static str { //! fn name(&self) -> &'static str {
//! "onConnect" //! "onConnect"
//! } //! }
//! //!
//! /// Function will be executed when client connected //! /// Event function
//! async fn execute(&self, client: &mut Client) { //! async fn execute(&self, client: &mut Client) {
//! client //! client
//! .send(&format!("Welcome {}", client.stream.peer_addr().unwrap())) //! .send(&format!("Welcome {}", client.stream.peer_addr().unwrap()))
@ -134,17 +114,11 @@
//! } //! }
//! } //! }
//! //!
//! /// Register plugin and command //! /// Regsiter plugin
//! #[no_mangle] //! #[no_mangle]
//! pub fn plugin_entry( //! pub fn plugin_entry(registrar: &mut dyn Registrar) {
//! plugin: &mut dyn PluginRegistrar, //! registrar.register_plugin(Box::new(PluginTest));
//! _command: &mut dyn CommandRegistrar, //! registrar.register_event(Box::new(PluginTest));
//! event: &mut dyn EventRegistrar,
//! ) {
//! // register plugin
//! plugin.register(Box::new(PluginTest));
//! // register event
//! event.register(Box::new(PluginTest));
//! } //! }
//! ``` //! ```
//! //!
@ -157,5 +131,7 @@
//! Move compiled plugin to the `plugin` directory where servers is located //! Move compiled plugin to the `plugin` directory where servers is located
mod loader; mod loader;
mod types;
pub use loader::*; pub use loader::*;
pub use types::*;

94
src/plugins/types.rs Normal file
View File

@ -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<Box<dyn Plugin>>,
/// Vector with all commands.
pub commands: Vec<Box<dyn Command>>,
/// Vector with all events.
pub events: Vec<Box<dyn Event>>,
}
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<PluginManager>;
/// Plugin Registrar
pub trait Registrar {
/// Function to register the plugin
fn register_plugin(&mut self, plugin: Box<dyn Plugin>);
/// Function to register the command
fn register_command(&mut self, command: Box<dyn Command>);
/// Function to register the event
fn register_event(&mut self, event: Box<dyn Event>);
}
impl Registrar for PluginManager {
fn register_plugin(&mut self, plugin: Box<dyn Plugin>) {
self.plugins.push(plugin)
}
fn register_command(&mut self, command: Box<dyn Command>) {
self.commands.push(command)
}
fn register_event(&mut self, event: Box<dyn Event>) {
self.events.push(event)
}
}

View File

@ -2,27 +2,26 @@ use std::io::Write;
use log::trace; use log::trace;
use crate::plugins::{CommandManagerType, EventManagerType}; use crate::plugins::PluginManagerType;
use super::Client; use super::Client;
/// Handle Client connection /// Handle Client connection
pub async fn handle_connection( pub async fn handle_connection(
mut client: Client, mut client: Client,
commands: CommandManagerType, plugin_manager: PluginManagerType,
events: EventManagerType,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
println!("New Client: {:?}", client.stream.peer_addr()?); println!("New Client: {:?}", client.stream.peer_addr()?);
// run `onConnect` events from plugins // run `onConnect` events from plugins
check_event(&mut client, &events, "onConnect").await; check_event(&mut client, &plugin_manager, "onConnect").await;
loop { loop {
// read client message/buffer // read client message/buffer
let buf = client.read()?; let buf = client.read()?;
// run `onSend` events from plugins // run `onSend` events from plugins
check_event(&mut client, &events, "onSend").await; check_event(&mut client, &plugin_manager, "onSend").await;
// split message by whitespaces // split message by whitespaces
let args: Vec<&str> = buf.split_ascii_whitespace().collect(); 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"); client.send("empty buffer").expect("send message");
// don't execute the following commands because it causes panic // don't execute the following commands because it causes panic
continue continue;
} }
// get command from args // get command from args
let cmd = args[0]; let cmd = args[0];
// search if a command exists // 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 this is the entered command
if cmd == command.name() { if cmd == command.name() {
trace!("Executing a command `{}`", command.name()); trace!("Executing a command `{}`", command.name());
// execute command // execute command
command command
.execute(&mut client, args[1..args.len()].to_vec(), &commands) .execute(&mut client, args[1..args.len()].to_vec(), &plugin_manager)
.await; .await;
// don't search for more commands // don't search for more commands
@ -65,7 +64,7 @@ pub async fn handle_connection(
} }
/// Search for a events and execute it /// 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() { for event in events.events.iter() {
// check if this event should be started // check if this event should be started
if event.name() == event_name { if event.name() == event_name {