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 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));
}

View File

@ -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");

View File

@ -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

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 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<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)
}
}
use super::{PluginManager, PluginManagerType};
/// 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
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<unsafe extern "C" fn(&mut dyn Registrar) -> ()> =
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))
}

View File

@ -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::*;

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 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 {