refactor code and add debug and trace logger

This commit is contained in:
MedzikUser 2022-06-05 16:08:13 +02:00
parent 3975102736
commit 969be3498b
No known key found for this signature in database
GPG Key ID: A5FAC1E185C112DB
12 changed files with 122 additions and 95 deletions

36
Cargo.lock generated
View File

@ -87,6 +87,17 @@ dependencies = [
"os_str_bytes", "os_str_bytes",
] ]
[[package]]
name = "colored"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3616f750b84d8f0de8a58bda93e08e2a81ad3f523089b05f1dffecab48c6cbd"
dependencies = [
"atty",
"lazy_static",
"winapi",
]
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.11.2" version = "0.11.2"
@ -236,9 +247,22 @@ dependencies = [
"clap", "clap",
"libloading", "libloading",
"log", "log",
"simple_logger",
"tokio", "tokio",
] ]
[[package]]
name = "simple_logger"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75a9723083573ace81ad0cdfc50b858aa3c366c48636edb4109d73122a0c0ea"
dependencies = [
"atty",
"colored",
"log",
"winapi",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.10.0" version = "0.10.0"
@ -280,6 +304,18 @@ dependencies = [
"num_cpus", "num_cpus",
"once_cell", "once_cell",
"pin-project-lite", "pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
"syn",
] ]
[[package]] [[package]]

View File

@ -11,5 +11,6 @@ anyhow = "1.0.57"
async-trait = "0.1.56" async-trait = "0.1.56"
clap = { version = "3.1.18", features = ["derive"] } clap = { version = "3.1.18", features = ["derive"] }
libloading = "0.7.3" libloading = "0.7.3"
log = "0.4.17" log = { version = "0.4.17", features = ["release_max_level_info", "max_level_trace"] }
tokio = { version = "1.19.0", features = ["rt-multi-thread"] } simple_logger = { version = "2.1.0", default-features = false, features = ["colors"] }
tokio = { version = "1.19.0", features = ["rt-multi-thread", "macros"] }

View File

@ -1,6 +1,7 @@
use async_trait::async_trait; use async_trait::async_trait;
use servers::{ use servers::{
tcp::Client, Command, CommandManagerType, CommandRegistrar, Plugin, PluginRegistrar, plugins::{Command, CommandManagerType, CommandRegistrar, Plugin, PluginRegistrar},
tcp::Client,
}; };
struct PluginTest; struct PluginTest;

View File

@ -1,36 +0,0 @@
use std::{any::Any, sync::Arc};
use async_trait::async_trait;
use crate::tcp::Client;
#[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>, commands: &CommandManagerType);
}
pub struct CommandManager {
/// Vector with plugins
pub commands: Vec<Box<dyn Command>>,
}
impl CommandManager {
pub fn new() -> Self {
Self {
commands: Vec::new(),
}
}
}
impl Default for CommandManager {
fn default() -> Self {
Self::new()
}
}
pub type CommandManagerType = Arc<Vec<Box<dyn Command>>>;

View File

@ -1,6 +1,9 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{command_handler::Command, tcp::Client, CommandManagerType}; use crate::{
plugins::{Command, CommandManagerType},
tcp::Client,
};
pub struct CommandHelp; pub struct CommandHelp;
@ -14,8 +17,13 @@ impl Command for CommandHelp {
"show help" "show help"
} }
async fn execute(&self, client: &mut Client, _args: Vec<&str>, commands: &CommandManagerType) { async fn execute(
for command in commands.iter() { &self,
client: &mut Client,
_args: Vec<&str>,
command_manager: &CommandManagerType,
) {
for command in command_manager.commands.iter() {
client client
.send(&format!("{} - {}", command.name(), command.help())) .send(&format!("{} - {}", command.name(), command.help()))
.await .await

View File

@ -1,15 +1,9 @@
#![allow(clippy::vec_init_then_push)]
mod help; mod help;
pub use help::*; pub use help::*;
use crate::command_handler::Command; use crate::plugins::Command;
pub fn register_commands() -> Vec<Box<dyn Command>> { pub fn register_commands() -> Vec<Box<dyn Command>> {
let mut commands: Vec<Box<dyn Command>> = Vec::new(); vec![Box::new(CommandHelp)]
commands.push(Box::new(CommandHelp));
commands
} }

View File

@ -1,8 +1,3 @@
mod command_handler; pub mod commands;
mod commands; pub mod plugins;
mod plugin_loader;
pub use command_handler::*;
pub use plugin_loader::*;
pub mod tcp; pub mod tcp;

View File

@ -1,5 +1,6 @@
use clap::Parser; use clap::Parser;
use servers::tcp; use servers::tcp;
use simple_logger::SimpleLogger;
#[derive(Parser)] #[derive(Parser)]
#[clap( #[clap(
@ -26,6 +27,9 @@ struct Cli {
} }
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
SimpleLogger::new().init()?;
// parse cli args
let cli = Cli::parse(); let cli = Cli::parse();
// start tcp server // start tcp server

View File

@ -4,7 +4,7 @@ use async_trait::async_trait;
use libloading::{Library, Symbol}; use libloading::{Library, Symbol};
use log::{debug, trace}; use log::{debug, trace};
use crate::{commands, Command, CommandManager, CommandManagerType}; use crate::{commands, tcp::Client};
/// A plugin which allows you to add extra functionality. /// A plugin which allows you to add extra functionality.
#[async_trait] #[async_trait]
@ -20,7 +20,6 @@ pub trait Plugin: Any + Send + Sync {
} }
pub struct PluginManager { pub struct PluginManager {
/// Vector with loaded plugins
pub plugins: Vec<Box<dyn Plugin>>, pub plugins: Vec<Box<dyn Plugin>>,
} }
@ -31,17 +30,6 @@ impl PluginManager {
plugins: Vec::new(), plugins: Vec::new(),
} }
} }
/// Unload all plugins and loaded plugin libraries, making sure to fire
/// their `on_plugin_unload()` methods so they can do any necessary cleanup.
pub async fn unload(&mut self) {
debug!("Unloading plugins");
for plugin in self.plugins.drain(..) {
trace!("Firing on_plugin_unload for {:?}", plugin.name());
plugin.on_plugin_unload().await;
}
}
} }
impl Default for PluginManager { impl Default for PluginManager {
@ -60,6 +48,42 @@ impl PluginRegistrar for PluginManager {
} }
} }
#[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,
);
}
pub struct CommandManager {
/// Vector with plugins
pub commands: Vec<Box<dyn Command>>,
}
impl CommandManager {
pub fn new() -> Self {
Self {
commands: Vec::new(),
}
}
}
pub type CommandManagerType = Arc<CommandManager>;
impl Default for CommandManager {
fn default() -> Self {
Self::new()
}
}
pub trait CommandRegistrar { pub trait CommandRegistrar {
fn register_plugin(&mut self, command: Box<dyn Command>); fn register_plugin(&mut self, command: Box<dyn Command>);
} }
@ -70,9 +94,7 @@ impl CommandRegistrar for CommandManager {
} }
} }
pub type PluginManagerType = Arc<Vec<Box<dyn Plugin>>>; pub fn loader() -> anyhow::Result<(Arc<CommandManager>, Arc<PluginManager>)> {
pub fn loader() -> anyhow::Result<(PluginManagerType, CommandManagerType)> {
// 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)?;
@ -93,33 +115,28 @@ pub fn loader() -> anyhow::Result<(PluginManagerType, CommandManagerType)> {
// get library file path // get library file path
let path = path?.path(); let path = path?.path();
let plugin_path = path.to_str().unwrap_or("unknown");
// log debug info
debug!("Loading plugin `{}`", plugin_path);
// loading library with .so is unsafe // loading library with .so is unsafe
unsafe { unsafe {
// load library // load library
// Box::new and Box::leak must be there because if it isn't there it throws a segmentation fault // Box::new and Box::leak must be there because if it isn't there it throws a segmentation fault
let lib = Box::leak(Box::new(Library::new(path)?)); let lib = Box::leak(Box::new(Library::new(&path)?));
// get `plugin_entry` from library // get `plugin_entry` from library
trace!("Finding symbol `plugin_entry` in `{}`", plugin_path);
let func: Symbol< let func: Symbol<
unsafe extern "C" fn(&mut dyn PluginRegistrar, &mut dyn CommandRegistrar) -> (), unsafe extern "C" fn(&mut dyn PluginRegistrar, &mut dyn CommandRegistrar) -> (),
> = lib.get(b"plugin_entry")?; > = lib.get(b"plugin_entry")?;
// execute initial function // execute initial plugin function
trace!("Running `plugin_entry(...)` in plugin `{}`", plugin_path);
func(&mut plugin_manager, &mut command_manager); func(&mut plugin_manager, &mut command_manager);
} }
} }
// create Arc in Vector Ok((Arc::new(command_manager), Arc::new(plugin_manager)))
let mut commands = Vec::new();
for command in command_manager.commands {
commands.push(command)
}
// create Arc in Vector
let mut plugins = Vec::new();
for plugin in plugin_manager.plugins {
plugins.push(plugin)
}
Ok((Arc::new(plugins), Arc::new(commands)))
} }

3
src/plugins/mod.rs Normal file
View File

@ -0,0 +1,3 @@
mod loader;
pub use loader::*;

View File

@ -1,6 +1,8 @@
use std::io::Write; use std::io::Write;
use crate::CommandManagerType; use log::trace;
use crate::plugins::CommandManagerType;
use super::Client; use super::Client;
@ -21,10 +23,11 @@ pub async fn handle_connection(
let cmd = args[0]; let cmd = args[0];
// search if a command exists // search if a command exists
for command in commands.iter() { for command in commands.commands.iter() {
// if this is the entered command // if this is the entered command
if cmd == command.name() { if cmd == command.name() {
// execute command // execute command
trace!("Executing a command `{}`", command.name());
command command
.execute(&mut client, args[1..args.len()].to_vec(), &commands) .execute(&mut client, args[1..args.len()].to_vec(), &commands)
.await; .await;

View File

@ -1,27 +1,28 @@
use std::net::TcpListener; use std::net::TcpListener;
use crate::{ use crate::{
loader, plugins::loader,
tcp::{handle_connection, Client}, tcp::{handle_connection, Client},
}; };
pub fn start_server(host: &str, port: &str) -> anyhow::Result<()> { #[tokio::main]
pub async fn start_server(host: &str, port: &str) -> anyhow::Result<()> {
// listen Tcp server // listen Tcp server
let listener = TcpListener::bind(format!("{host}:{port}"))?; let listener = TcpListener::bind(format!("{host}:{port}"))?;
println!("Tcp server started at: {}", listener.local_addr()?); println!("Tcp server started at: {}", listener.local_addr()?);
// load plugins and commands // load plugins and commands
let (_plugin_manager, commands_manager) = loader()?; let (command_manager, _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);
let commands_manager = commands_manager.clone(); let command_manager = command_manager.clone();
// handle client connection in new thread // handle client connection in new thread
tokio::spawn(handle_connection(client, commands_manager)); tokio::spawn(handle_connection(client, command_manager));
} }
Ok(()) Ok(())