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",
]
[[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]]
name = "hashbrown"
version = "0.11.2"
@ -236,9 +247,22 @@ dependencies = [
"clap",
"libloading",
"log",
"simple_logger",
"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]]
name = "strsim"
version = "0.10.0"
@ -280,6 +304,18 @@ dependencies = [
"num_cpus",
"once_cell",
"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]]

View File

@ -11,5 +11,6 @@ anyhow = "1.0.57"
async-trait = "0.1.56"
clap = { version = "3.1.18", features = ["derive"] }
libloading = "0.7.3"
log = "0.4.17"
tokio = { version = "1.19.0", features = ["rt-multi-thread"] }
log = { version = "0.4.17", features = ["release_max_level_info", "max_level_trace"] }
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 servers::{
tcp::Client, Command, CommandManagerType, CommandRegistrar, Plugin, PluginRegistrar,
plugins::{Command, CommandManagerType, CommandRegistrar, Plugin, PluginRegistrar},
tcp::Client,
};
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 crate::{command_handler::Command, tcp::Client, CommandManagerType};
use crate::{
plugins::{Command, CommandManagerType},
tcp::Client,
};
pub struct CommandHelp;
@ -14,8 +17,13 @@ impl Command for CommandHelp {
"show help"
}
async fn execute(&self, client: &mut Client, _args: Vec<&str>, commands: &CommandManagerType) {
for command in commands.iter() {
async fn execute(
&self,
client: &mut Client,
_args: Vec<&str>,
command_manager: &CommandManagerType,
) {
for command in command_manager.commands.iter() {
client
.send(&format!("{} - {}", command.name(), command.help()))
.await

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ use async_trait::async_trait;
use libloading::{Library, Symbol};
use log::{debug, trace};
use crate::{commands, Command, CommandManager, CommandManagerType};
use crate::{commands, tcp::Client};
/// A plugin which allows you to add extra functionality.
#[async_trait]
@ -20,7 +20,6 @@ pub trait Plugin: Any + Send + Sync {
}
pub struct PluginManager {
/// Vector with loaded plugins
pub plugins: Vec<Box<dyn Plugin>>,
}
@ -31,17 +30,6 @@ impl PluginManager {
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 {
@ -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 {
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<(PluginManagerType, CommandManagerType)> {
pub fn loader() -> anyhow::Result<(Arc<CommandManager>, Arc<PluginManager>)> {
// get path to .so lib from command argument
let config_dir = "./plugins";
let paths = fs::read_dir(config_dir)?;
@ -93,33 +115,28 @@ pub fn loader() -> anyhow::Result<(PluginManagerType, CommandManagerType)> {
// get library file 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
unsafe {
// load library
// 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
trace!("Finding symbol `plugin_entry` in `{}`", plugin_path);
let func: Symbol<
unsafe extern "C" fn(&mut dyn PluginRegistrar, &mut dyn CommandRegistrar) -> (),
> = 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);
}
}
// create Arc in Vector
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)))
Ok((Arc::new(command_manager), Arc::new(plugin_manager)))
}

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

View File

@ -1,27 +1,28 @@
use std::net::TcpListener;
use crate::{
loader,
plugins::loader,
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
let listener = TcpListener::bind(format!("{host}:{port}"))?;
println!("Tcp server started at: {}", listener.local_addr()?);
// load plugins and commands
let (_plugin_manager, commands_manager) = loader()?;
let (command_manager, _plugin_manager) = loader()?;
// Accepts a new incoming connection from this listener.
while let Ok((stream, _address)) = listener.accept() {
let client = Client::new(stream);
let commands_manager = commands_manager.clone();
let command_manager = command_manager.clone();
// handle client connection in new thread
tokio::spawn(handle_connection(client, commands_manager));
tokio::spawn(handle_connection(client, command_manager));
}
Ok(())