diff --git a/Cargo.lock b/Cargo.lock index c622f1b..1ee1948 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -474,7 +474,6 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" name = "plugin_test" version = "0.1.0" dependencies = [ - "async-trait", "servers", ] diff --git a/plugin_test/Cargo.toml b/plugin_test/Cargo.toml index d7e0b38..1bebcf2 100644 --- a/plugin_test/Cargo.toml +++ b/plugin_test/Cargo.toml @@ -7,5 +7,4 @@ edition = "2021" crate-type = ["dylib"] [dependencies] -async-trait = "0.1.56" servers = { path = ".." } diff --git a/plugin_test/src/lib.rs b/plugin_test/src/lib.rs index 04fc864..254bf11 100644 --- a/plugin_test/src/lib.rs +++ b/plugin_test/src/lib.rs @@ -1,7 +1,6 @@ -use async_trait::async_trait; use servers::{ plugins::{Command, Event, Plugin, PluginManagerType, Registrar, Result}, - tcp::Client, + tcp::Client, async_trait, }; struct PluginTest; @@ -29,7 +28,7 @@ impl Command for PluginTest { /// Help message of the command fn help(&self) -> &'static str { - "test command" + "Test command from plugin" } /// Command function @@ -55,7 +54,9 @@ impl Event for PluginTest { /// Event function async fn execute(&self, client: &mut Client) -> Result<()> { - client.send(&format!("Welcome {}", client.stream.peer_addr().unwrap())).await?; + client + .send(&format!("Welcome {}", client.stream.peer_addr().unwrap())) + .await?; Ok(()) } diff --git a/src/commands/disconnect.rs b/src/commands/disconnect.rs new file mode 100644 index 0000000..767de20 --- /dev/null +++ b/src/commands/disconnect.rs @@ -0,0 +1,32 @@ +use async_trait::async_trait; +use tokio::io::AsyncWriteExt; + +use crate::{ + plugins::{Command, PluginManagerType, Result}, + tcp::Client, +}; + +pub struct CommandDisconnect; + +#[async_trait] +impl Command for CommandDisconnect { + fn name(&self) -> &'static str { + "/disconnect" + } + + fn help(&self) -> &'static str { + "Disconnect from the server" + } + + async fn execute( + &self, + client: &mut Client, + _args: Vec<&str>, + _plugin_manager: &PluginManagerType, + ) -> Result<()> { + // close the connection + client.stream.shutdown().await?; + + Ok(()) + } +} diff --git a/src/commands/help.rs b/src/commands/help.rs index d5be081..d06ad76 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -14,7 +14,7 @@ impl Command for CommandHelp { } fn help(&self) -> &'static str { - "show help" + "Display all available commands" } async fn execute( @@ -23,10 +23,17 @@ impl Command for CommandHelp { _args: Vec<&str>, plugin_manager: &PluginManagerType, ) -> Result<()> { + // Vector which will contain help messages of the commands + let mut help = Vec::new(); + for command in plugin_manager.commands.iter() { - client.send(&format!("{} - {}", command.name(), command.help())).await?; + // add a help message for the command + help.push(format!("{} - {}", command.name(), command.help())); } + // send help message to the client + client.send(help.join("\n\r")).await?; + Ok(()) } } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 3a15ce3..28d73b7 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,5 +1,6 @@ //! Build-in commands +mod disconnect; mod help; use crate::plugins::Command; @@ -7,5 +8,8 @@ use crate::plugins::Command; /// Register build-in commands pub fn register_commands() -> Vec> { // create array with build-in commands - vec![Box::new(help::CommandHelp)] + vec![ + Box::new(help::CommandHelp), + Box::new(disconnect::CommandDisconnect), + ] } diff --git a/src/lib.rs b/src/lib.rs index 2b80c0f..3e1a7e1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,9 +20,11 @@ //! Go to [plugins](plugins) module #![doc(html_root_url = "https://servers.medzik.xyz")] -#![warn(missing_docs)] pub mod commands; +pub mod logger; +mod macros; pub mod plugins; pub mod tcp; -mod macros; + +pub use async_trait::async_trait; diff --git a/src/logger.rs b/src/logger.rs new file mode 100644 index 0000000..2760ced --- /dev/null +++ b/src/logger.rs @@ -0,0 +1,9 @@ +use tracing::metadata::LevelFilter; + +pub fn init() { + better_panic::install(); + + tracing_subscriber::fmt() + .with_max_level(LevelFilter::TRACE) + .init(); +} diff --git a/src/main.rs b/src/main.rs index e954c4b..b4179d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use clap::Parser; use cli::Cli; use servers::{ + logger, plugins::loader, tcp::{handle_connection, handle_websocket, Client}, }; @@ -11,10 +12,7 @@ mod cli; #[tokio::main] async fn main() -> anyhow::Result<()> { - // init better panic - better_panic::install(); - // init logger - tracing_subscriber::fmt().init(); + logger::init(); // parse cli args let args = Cli::parse(); @@ -29,13 +27,13 @@ async fn main() -> anyhow::Result<()> { } // start tcp server - start_tcp_server(&args.host, &args.port).await?; + start_tcp_server(args.host, args.port).await?; Ok(()) } /// Start tcp server -async fn start_tcp_server(host: &str, port: &str) -> anyhow::Result<()> { +async fn start_tcp_server(host: String, port: String) -> anyhow::Result<()> { // listen Tcp server let listener = TcpListener::bind(format!("{host}:{port}")).await?; @@ -51,11 +49,14 @@ async fn start_tcp_server(host: &str, port: &str) -> anyhow::Result<()> { // handle client connection in new thread tokio::spawn(async move { - let ip = client.stream.peer_addr().unwrap(); + // get ip address of the client + let ip = client + .stream + .peer_addr() + .expect("failed to get peer address"); - match handle_connection(client, plugin_manager).await { - Ok(_) => (), - Err(err) => error!("Client {}, {}", ip, err), + if let Err(e) = handle_connection(client, plugin_manager).await { + error!("Client {ip}: {e}") } }); } @@ -75,11 +76,11 @@ async fn start_ws_server(host: String, port: String, tcp_port: String) -> anyhow while let Ok((stream, _address)) = listener.accept().await { let tcp_port = tcp_port.clone(); tokio::spawn(async { - let ip = stream.peer_addr().unwrap(); + // get ip address of the client + let ip = stream.peer_addr().expect("failed to get peer address"); - match handle_websocket(stream, tcp_port).await { - Ok(_) => (), - Err(err) => error!("Client {}, {}", ip, err), + if let Err(e) = handle_websocket(stream, tcp_port).await { + error!("Client {ip}: {e}") } }); } diff --git a/src/plugins/loader.rs b/src/plugins/loader.rs index ff6ef1b..7c20603 100644 --- a/src/plugins/loader.rs +++ b/src/plugins/loader.rs @@ -1,7 +1,7 @@ -use std::{fs, sync::Arc, path::Path}; +use std::{fs, path::Path, sync::Arc}; use libloading::{Library, Symbol}; -use tracing::{debug, trace}; +use tracing::{info, trace}; use crate::{commands, plugins::Registrar}; @@ -33,7 +33,7 @@ pub fn loader() -> anyhow::Result { let path = path?.path(); let plugin_path = path.to_str().unwrap(); - debug!("Loading plugin `{}`", plugin_path); + info!("Loading plugin `{}`", plugin_path); // loading library from .so is unsafe unsafe { @@ -41,13 +41,16 @@ pub fn loader() -> anyhow::Result { // 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)?)); - // get `plugin_entry` from library + // get function `plugin_entry` from library trace!("Finding symbol `plugin_entry` in `{}`", plugin_path); let func: Symbol ()> = lib.get(b"plugin_entry")?; // execute initial plugin function - trace!("Running `plugin_entry(...)` in plugin `{}`", plugin_path); + trace!( + "Running function `plugin_entry` from plugin `{}`", + plugin_path + ); func(&mut plugin_manager); } } diff --git a/src/plugins/mod.rs b/src/plugins/mod.rs index 73a8e71..32d6da0 100644 --- a/src/plugins/mod.rs +++ b/src/plugins/mod.rs @@ -15,15 +15,13 @@ //! //! ```toml //! [dependencies] -//! async-trait = "0.1.56" -//! servers = "0.1.0" +//! servers = { git = "https://github.com/MedzikUser/servers" } //! ``` //! //! In file `src/lib.rs` //! //! ```no_run -//! use async_trait::async_trait; -//! use servers::{plugins::{Plugin, Registrar}, tcp::Client}; +//! use servers::{plugins::{Plugin, Registrar}, tcp::Client, async_trait}; //! //! struct PluginTest; //! @@ -50,9 +48,8 @@ //! ## Add command //! //! ```no_run -//! use async_trait::async_trait; //! use servers::{ -//! plugins::{Command, PluginManagerType, Registrar, Result}, +//! plugins::{Command, PluginManagerType, Registrar, Result, async_trait}, //! tcp::Client, //! }; //! # @@ -104,9 +101,8 @@ //! In file `src/lib.rs` //! //! ```no_run -//! use async_trait::async_trait; //! use servers::{ -//! plugins::{Event, Registrar, Result}, +//! plugins::{Event, Registrar, Result, async_trait}, //! tcp::Client, //! }; //! # diff --git a/src/tcp/client.rs b/src/tcp/client.rs index 875ec66..d593674 100644 --- a/src/tcp/client.rs +++ b/src/tcp/client.rs @@ -1,4 +1,9 @@ -use tokio::{net::TcpStream, io::{self, AsyncWriteExt, AsyncReadExt}}; +use core::fmt; + +use tokio::{ + io::{self, AsyncReadExt, AsyncWriteExt}, + net::TcpStream, +}; /// Max size of a TCP packet pub const MAX_PACKET_LEN: usize = 65536; @@ -33,7 +38,10 @@ impl Client { } /// Send message to the client - pub async fn send(&mut self, content: &str) -> io::Result<()> { + pub async fn send(&mut self, content: S) -> io::Result<()> + where + S: ToString + fmt::Debug + fmt::Display, + { // add a new line at the end of the content let content = format!("{content}\n\r"); diff --git a/src/tcp/handle_connection.rs b/src/tcp/handle_connection.rs index c499791..96b3339 100644 --- a/src/tcp/handle_connection.rs +++ b/src/tcp/handle_connection.rs @@ -1,5 +1,5 @@ -use tracing::{error, info, trace}; use tokio::io::AsyncWriteExt; +use tracing::{error, info, trace}; use crate::plugins::PluginManagerType; diff --git a/src/tcp/handle_websocket.rs b/src/tcp/handle_websocket.rs index d5d9c15..a97d982 100644 --- a/src/tcp/handle_websocket.rs +++ b/src/tcp/handle_websocket.rs @@ -4,7 +4,6 @@ use futures_util::{ stream::{SplitSink, SplitStream}, SinkExt, StreamExt, }; -use tracing::info; use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, net::{ @@ -13,6 +12,7 @@ use tokio::{ }, }; use tokio_tungstenite::WebSocketStream; +use tracing::info; use tungstenite::Message; use super::MAX_PACKET_LEN;