diff --git a/Cargo.lock b/Cargo.lock index a69179b..46a4171 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -617,6 +617,7 @@ dependencies = [ "clap", "console-subscriber", "once_cell", + "thiserror", "tokio", "tracing", "tracing-subscriber", @@ -1089,6 +1090,26 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thread_local" version = "1.1.9" diff --git a/Cargo.toml b/Cargo.toml index f252e93..466f79a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] -anyhow = "1.0.100" async-trait = "0.1.89" clap = { version = "4.5.48", features = ["derive"] } once_cell = "1.21.3" @@ -12,6 +11,8 @@ tokio = { version = "1.47.1", features = ["full"] } console-subscriber = { version = "0.4.1", optional = true } tracing = "0.1.41" tracing-subscriber = "0.3.20" +thiserror = "2.0.17" +anyhow = "1.0.100" [features] tokio-console = ["tokio/tracing", "console-subscriber"] diff --git a/src/channels.rs b/src/channels.rs index 05a8b5e..c1a804a 100644 --- a/src/channels.rs +++ b/src/channels.rs @@ -1,9 +1,8 @@ use std::collections::BTreeSet; -use anyhow::Result; use tokio::{io::BufWriter, net::TcpStream}; -use crate::{sender::IrcResponseCodes, user::User}; +use crate::{error_structs::SenderError, sender::IrcResponseCodes, user::User}; #[derive(Clone, Debug, Hash, Eq, PartialEq)] pub struct Channel { @@ -28,7 +27,7 @@ impl Channel { user: User, writer: &mut BufWriter, hostname: &str, - ) -> Result<()> { + ) -> Result<(), SenderError> { let mut members = Vec::new(); for member in self.clone().joined_users { @@ -58,7 +57,7 @@ impl Channel { user: User, writer: &mut BufWriter, hostname: &str, - ) -> Result<()> { + ) -> Result<(), SenderError> { IrcResponseCodes::NoTopic .into_irc_response( user.nickname.clone().unwrap(), diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 6b2ec0a..9caa9f7 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,7 +1,6 @@ #![allow(dead_code)] use std::collections::HashMap; -use anyhow::{Result, anyhow}; use async_trait::async_trait; use tokio::{io::BufWriter, net::TcpStream, sync::broadcast::Sender}; @@ -11,6 +10,7 @@ use crate::{ commands::{ cap::Cap, join::Join, nick::Nick, ping::Ping, privmsg::PrivMsg, user::User as UserHandler, }, + error_structs::CommandExecError, messages::Message, sender::IrcResponse, user::User, @@ -94,7 +94,7 @@ impl IrcCommand { writer: &mut BufWriter, hostname: &str, user_state: &mut User, - ) -> Result<()> { + ) -> Result<(), CommandExecError> { let mut command_map: HashMap = HashMap::new(); let broadcast_sender = SENDER.lock().await.clone().unwrap(); @@ -111,7 +111,7 @@ impl IrcCommand { let command_to_execute = command_map .get(&self.command.to_uppercase()) .map(|v| *v) - .ok_or(anyhow!("unknown command!"))?; + .ok_or(CommandExecError::NonexistantCommand)?; let action = command_to_execute .handle( diff --git a/src/error_structs.rs b/src/error_structs.rs new file mode 100644 index 0000000..1660520 --- /dev/null +++ b/src/error_structs.rs @@ -0,0 +1,43 @@ +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum HandlerError { + #[error("std::io error")] + StdIoError(#[from] std::io::Error), +} + +#[derive(Error, Debug)] +pub enum ListenerError { + #[error("connection error")] + ConnectionError, + + #[error("user has not identified yet")] + UserIsUnidentified, +} + +#[derive(Error, Debug)] +pub enum SenderError { + #[error("std::io error")] + StdIoError(#[from] std::io::Error), +} + +#[derive(Error, Debug)] +pub enum CommandExecError { + #[error("command does not exist")] + NonexistantCommand, +} + +// Conversion impls here +impl From for ListenerError { + fn from(value: SenderError) -> Self { + match value { + SenderError::StdIoError(_) => Self::ConnectionError, + } + } +} + +impl From for ListenerError { + fn from(_: std::io::Error) -> Self { + Self::ConnectionError + } +} diff --git a/src/login.rs b/src/login.rs index 6d912de..5fab819 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,13 +1,12 @@ -use anyhow::Result; use tokio::{io::BufWriter, net::TcpStream}; -use crate::{ServerInfo, sender::IrcResponseCodes, user::User}; +use crate::{ServerInfo, error_structs::SenderError, sender::IrcResponseCodes, user::User}; pub async fn send_motd( server_info: ServerInfo, user_info: User, writer: &mut BufWriter, -) -> Result<()> { +) -> Result<(), SenderError> { let user_info = user_info.unwrap_all(); let server_version = &format!("IRS-v{}", env!("CARGO_PKG_VERSION")) as &str; diff --git a/src/main.rs b/src/main.rs index 5d1cfa2..4aed1ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::{ str::FromStr, }; -use anyhow::{Result, bail}; +use anyhow::Error as AnyhowError; use once_cell::sync::Lazy; use tokio::{ io::{AsyncBufReadExt, BufReader as TokioBufReader, BufWriter as TokioBufWriter}, @@ -19,6 +19,7 @@ use tracing::instrument; use crate::{ channels::Channel, + error_structs::{HandlerError, ListenerError}, login::send_motd, messages::Message, sender::{IrcResponse, IrcResponseCodes}, @@ -27,6 +28,7 @@ use crate::{ mod channels; mod commands; +mod error_structs; mod login; mod messages; mod sender; @@ -49,7 +51,7 @@ struct ServerInfo { } #[tokio::main] -async fn main() -> Result<()> { +async fn main() -> Result<(), AnyhowError> { #[cfg(feature = "tokio-console")] console_subscriber::init(); @@ -83,7 +85,11 @@ async fn main() -> Result<()> { } #[instrument] -async fn handle_connection(stream: TcpStream, info: ServerInfo, tx: Sender) -> Result<()> { +async fn handle_connection( + stream: TcpStream, + info: ServerInfo, + tx: Sender, +) -> Result<(), HandlerError> { let stream_tcp = stream.try_clone()?; let mut message_receiver = tx.clone().subscribe(); let mut tcp_reader = TokioBufReader::new(TokioTcpStream::from_std(stream.try_clone()?)?); @@ -106,8 +112,14 @@ async fn handle_connection(stream: TcpStream, info: ServerInfo, tx: Sender { match result { Ok(_) => {}, - Err(_) => { - // break; + Err(err) => { + match err { + ListenerError::ConnectionError => { + break; + } + + _ => {} + }; } } }, @@ -124,21 +136,21 @@ async fn tcp_listener( mut state: User, info: &ServerInfo, reader: &mut TokioBufReader, -) -> Result { +) -> Result { let mut buffer = String::new(); let mut writer = TokioBufWriter::new(TokioTcpStream::from_std(stream.try_clone()?)?); buffer.clear(); match reader.read_line(&mut buffer).await { - Ok(0) => bail!("invalid response"), + Ok(0) => return Err(ListenerError::ConnectionError), Ok(_) => {} Err(_) => { let mut conneted_users = CONNECTED_USERS.lock().await; let _ = conneted_users.remove(&state.clone().unwrap_all()); - bail!("client disconnected") + return Err(ListenerError::ConnectionError); } } @@ -177,9 +189,9 @@ async fn message_listener( user_wrapped: &User, receiver: &mut Receiver, writer: &mut TokioBufWriter, -) -> Result<()> { +) -> Result<(), ListenerError> { if !user_wrapped.is_populated() { - bail!("user has not registered yet, returning..."); + return Err(ListenerError::UserIsUnidentified); } let user = user_wrapped.clone().unwrap_all(); diff --git a/src/sender.rs b/src/sender.rs index 67bec6e..32a92f9 100644 --- a/src/sender.rs +++ b/src/sender.rs @@ -1,9 +1,10 @@ -use anyhow::Result; use tokio::{ io::{AsyncWriteExt, BufWriter}, net::TcpStream, }; +use crate::error_structs::SenderError; + #[derive(Clone)] pub struct IrcResponse { pub sender: Option, @@ -32,7 +33,7 @@ impl IrcResponse { hostname: &str, writer: &mut BufWriter, prepend_column: bool, - ) -> Result<()> { + ) -> Result<(), SenderError> { let sender = format!(":{}", self.sender.clone().unwrap_or(hostname.to_string())); let mut full_response = Vec::new();