diff --git a/Cargo.lock b/Cargo.lock index 3e86e06..41789c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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]] diff --git a/Cargo.toml b/Cargo.toml index c949be1..2e50b6d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/plugin_test/src/lib.rs b/plugin_test/src/lib.rs index af63328..a7412e5 100644 --- a/plugin_test/src/lib.rs +++ b/plugin_test/src/lib.rs @@ -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; diff --git a/src/command_handler.rs b/src/command_handler.rs deleted file mode 100644 index c2503d4..0000000 --- a/src/command_handler.rs +++ /dev/null @@ -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>, -} - -impl CommandManager { - pub fn new() -> Self { - Self { - commands: Vec::new(), - } - } -} - -impl Default for CommandManager { - fn default() -> Self { - Self::new() - } -} - -pub type CommandManagerType = Arc>>; diff --git a/src/commands/help.rs b/src/commands/help.rs index 4dc45fe..57083da 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -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 diff --git a/src/commands/mod.rs b/src/commands/mod.rs index a37551e..3f47a22 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -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> { - let mut commands: Vec> = Vec::new(); - - commands.push(Box::new(CommandHelp)); - - commands + vec![Box::new(CommandHelp)] } diff --git a/src/lib.rs b/src/lib.rs index 5b47e75..4d21673 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/main.rs b/src/main.rs index c79f59e..cf4a6be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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 diff --git a/src/plugin_loader.rs b/src/plugins/loader.rs similarity index 66% rename from src/plugin_loader.rs rename to src/plugins/loader.rs index 942f1bf..49601a1 100644 --- a/src/plugin_loader.rs +++ b/src/plugins/loader.rs @@ -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>, } @@ -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>, +} + +impl CommandManager { + pub fn new() -> Self { + Self { + commands: Vec::new(), + } + } +} + +pub type CommandManagerType = Arc; + +impl Default for CommandManager { + fn default() -> Self { + Self::new() + } +} + pub trait CommandRegistrar { fn register_plugin(&mut self, command: Box); } @@ -70,9 +94,7 @@ impl CommandRegistrar for CommandManager { } } -pub type PluginManagerType = Arc>>; - -pub fn loader() -> anyhow::Result<(PluginManagerType, CommandManagerType)> { +pub fn loader() -> anyhow::Result<(Arc, Arc)> { // 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))) } diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs new file mode 100644 index 0000000..4ff6f92 --- /dev/null +++ b/src/plugins/mod.rs @@ -0,0 +1,3 @@ +mod loader; + +pub use loader::*; diff --git a/src/tcp/handle_connection.rs b/src/tcp/handle_connection.rs index f77d03e..2d08710 100644 --- a/src/tcp/handle_connection.rs +++ b/src/tcp/handle_connection.rs @@ -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; diff --git a/src/tcp/start.rs b/src/tcp/start.rs index c571430..49e8817 100644 --- a/src/tcp/start.rs +++ b/src/tcp/start.rs @@ -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(())