feat: use thiserror for better error handling
This commit is contained in:
parent
86910cb29a
commit
8eed67ab69
8 changed files with 99 additions and 23 deletions
21
Cargo.lock
generated
21
Cargo.lock
generated
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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<TcpStream>,
|
||||
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<TcpStream>,
|
||||
hostname: &str,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), SenderError> {
|
||||
IrcResponseCodes::NoTopic
|
||||
.into_irc_response(
|
||||
user.nickname.clone().unwrap(),
|
||||
|
|
|
|||
|
|
@ -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<TcpStream>,
|
||||
hostname: &str,
|
||||
user_state: &mut User,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), CommandExecError> {
|
||||
let mut command_map: HashMap<String, &dyn IrcHandler> = 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(
|
||||
|
|
|
|||
43
src/error_structs.rs
Normal file
43
src/error_structs.rs
Normal file
|
|
@ -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<SenderError> for ListenerError {
|
||||
fn from(value: SenderError) -> Self {
|
||||
match value {
|
||||
SenderError::StdIoError(_) => Self::ConnectionError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for ListenerError {
|
||||
fn from(_: std::io::Error) -> Self {
|
||||
Self::ConnectionError
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TcpStream>,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), SenderError> {
|
||||
let user_info = user_info.unwrap_all();
|
||||
let server_version = &format!("IRS-v{}", env!("CARGO_PKG_VERSION")) as &str;
|
||||
|
||||
|
|
|
|||
32
src/main.rs
32
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<Message>) -> Result<()> {
|
||||
async fn handle_connection(
|
||||
stream: TcpStream,
|
||||
info: ServerInfo,
|
||||
tx: Sender<Message>,
|
||||
) -> 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<Messa
|
|||
result = message_listener(&state, &mut message_receiver, &mut tcp_writer) => {
|
||||
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<TokioTcpStream>,
|
||||
) -> Result<User> {
|
||||
) -> Result<User, ListenerError> {
|
||||
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<Message>,
|
||||
writer: &mut TokioBufWriter<TokioTcpStream>,
|
||||
) -> 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();
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
|
|
@ -32,7 +33,7 @@ impl IrcResponse {
|
|||
hostname: &str,
|
||||
writer: &mut BufWriter<TcpStream>,
|
||||
prepend_column: bool,
|
||||
) -> Result<()> {
|
||||
) -> Result<(), SenderError> {
|
||||
let sender = format!(":{}", self.sender.clone().unwrap_or(hostname.to_string()));
|
||||
let mut full_response = Vec::new();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue