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",
|
"clap",
|
||||||
"console-subscriber",
|
"console-subscriber",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
|
@ -1089,6 +1090,26 @@ version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
|
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]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
version = "1.1.9"
|
version = "1.1.9"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.100"
|
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
clap = { version = "4.5.48", features = ["derive"] }
|
clap = { version = "4.5.48", features = ["derive"] }
|
||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
|
|
@ -12,6 +11,8 @@ tokio = { version = "1.47.1", features = ["full"] }
|
||||||
console-subscriber = { version = "0.4.1", optional = true }
|
console-subscriber = { version = "0.4.1", optional = true }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.20"
|
tracing-subscriber = "0.3.20"
|
||||||
|
thiserror = "2.0.17"
|
||||||
|
anyhow = "1.0.100"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tokio-console = ["tokio/tracing", "console-subscriber"]
|
tokio-console = ["tokio/tracing", "console-subscriber"]
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
use tokio::{io::BufWriter, net::TcpStream};
|
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)]
|
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
|
||||||
pub struct Channel {
|
pub struct Channel {
|
||||||
|
|
@ -28,7 +27,7 @@ impl Channel {
|
||||||
user: User,
|
user: User,
|
||||||
writer: &mut BufWriter<TcpStream>,
|
writer: &mut BufWriter<TcpStream>,
|
||||||
hostname: &str,
|
hostname: &str,
|
||||||
) -> Result<()> {
|
) -> Result<(), SenderError> {
|
||||||
let mut members = Vec::new();
|
let mut members = Vec::new();
|
||||||
|
|
||||||
for member in self.clone().joined_users {
|
for member in self.clone().joined_users {
|
||||||
|
|
@ -58,7 +57,7 @@ impl Channel {
|
||||||
user: User,
|
user: User,
|
||||||
writer: &mut BufWriter<TcpStream>,
|
writer: &mut BufWriter<TcpStream>,
|
||||||
hostname: &str,
|
hostname: &str,
|
||||||
) -> Result<()> {
|
) -> Result<(), SenderError> {
|
||||||
IrcResponseCodes::NoTopic
|
IrcResponseCodes::NoTopic
|
||||||
.into_irc_response(
|
.into_irc_response(
|
||||||
user.nickname.clone().unwrap(),
|
user.nickname.clone().unwrap(),
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use tokio::{io::BufWriter, net::TcpStream, sync::broadcast::Sender};
|
use tokio::{io::BufWriter, net::TcpStream, sync::broadcast::Sender};
|
||||||
|
|
||||||
|
|
@ -11,6 +10,7 @@ use crate::{
|
||||||
commands::{
|
commands::{
|
||||||
cap::Cap, join::Join, nick::Nick, ping::Ping, privmsg::PrivMsg, user::User as UserHandler,
|
cap::Cap, join::Join, nick::Nick, ping::Ping, privmsg::PrivMsg, user::User as UserHandler,
|
||||||
},
|
},
|
||||||
|
error_structs::CommandExecError,
|
||||||
messages::Message,
|
messages::Message,
|
||||||
sender::IrcResponse,
|
sender::IrcResponse,
|
||||||
user::User,
|
user::User,
|
||||||
|
|
@ -94,7 +94,7 @@ impl IrcCommand {
|
||||||
writer: &mut BufWriter<TcpStream>,
|
writer: &mut BufWriter<TcpStream>,
|
||||||
hostname: &str,
|
hostname: &str,
|
||||||
user_state: &mut User,
|
user_state: &mut User,
|
||||||
) -> Result<()> {
|
) -> Result<(), CommandExecError> {
|
||||||
let mut command_map: HashMap<String, &dyn IrcHandler> = HashMap::new();
|
let mut command_map: HashMap<String, &dyn IrcHandler> = HashMap::new();
|
||||||
let broadcast_sender = SENDER.lock().await.clone().unwrap();
|
let broadcast_sender = SENDER.lock().await.clone().unwrap();
|
||||||
|
|
||||||
|
|
@ -111,7 +111,7 @@ impl IrcCommand {
|
||||||
let command_to_execute = command_map
|
let command_to_execute = command_map
|
||||||
.get(&self.command.to_uppercase())
|
.get(&self.command.to_uppercase())
|
||||||
.map(|v| *v)
|
.map(|v| *v)
|
||||||
.ok_or(anyhow!("unknown command!"))?;
|
.ok_or(CommandExecError::NonexistantCommand)?;
|
||||||
|
|
||||||
let action = command_to_execute
|
let action = command_to_execute
|
||||||
.handle(
|
.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 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(
|
pub async fn send_motd(
|
||||||
server_info: ServerInfo,
|
server_info: ServerInfo,
|
||||||
user_info: User,
|
user_info: User,
|
||||||
writer: &mut BufWriter<TcpStream>,
|
writer: &mut BufWriter<TcpStream>,
|
||||||
) -> Result<()> {
|
) -> Result<(), SenderError> {
|
||||||
let user_info = user_info.unwrap_all();
|
let user_info = user_info.unwrap_all();
|
||||||
let server_version = &format!("IRS-v{}", env!("CARGO_PKG_VERSION")) as &str;
|
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,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Result, bail};
|
use anyhow::Error as AnyhowError;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncBufReadExt, BufReader as TokioBufReader, BufWriter as TokioBufWriter},
|
io::{AsyncBufReadExt, BufReader as TokioBufReader, BufWriter as TokioBufWriter},
|
||||||
|
|
@ -19,6 +19,7 @@ use tracing::instrument;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
channels::Channel,
|
channels::Channel,
|
||||||
|
error_structs::{HandlerError, ListenerError},
|
||||||
login::send_motd,
|
login::send_motd,
|
||||||
messages::Message,
|
messages::Message,
|
||||||
sender::{IrcResponse, IrcResponseCodes},
|
sender::{IrcResponse, IrcResponseCodes},
|
||||||
|
|
@ -27,6 +28,7 @@ use crate::{
|
||||||
|
|
||||||
mod channels;
|
mod channels;
|
||||||
mod commands;
|
mod commands;
|
||||||
|
mod error_structs;
|
||||||
mod login;
|
mod login;
|
||||||
mod messages;
|
mod messages;
|
||||||
mod sender;
|
mod sender;
|
||||||
|
|
@ -49,7 +51,7 @@ struct ServerInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<(), AnyhowError> {
|
||||||
#[cfg(feature = "tokio-console")]
|
#[cfg(feature = "tokio-console")]
|
||||||
console_subscriber::init();
|
console_subscriber::init();
|
||||||
|
|
||||||
|
|
@ -83,7 +85,11 @@ async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument]
|
#[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 stream_tcp = stream.try_clone()?;
|
||||||
let mut message_receiver = tx.clone().subscribe();
|
let mut message_receiver = tx.clone().subscribe();
|
||||||
let mut tcp_reader = TokioBufReader::new(TokioTcpStream::from_std(stream.try_clone()?)?);
|
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) => {
|
result = message_listener(&state, &mut message_receiver, &mut tcp_writer) => {
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => {},
|
Ok(_) => {},
|
||||||
Err(_) => {
|
Err(err) => {
|
||||||
// break;
|
match err {
|
||||||
|
ListenerError::ConnectionError => {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -124,21 +136,21 @@ async fn tcp_listener(
|
||||||
mut state: User,
|
mut state: User,
|
||||||
info: &ServerInfo,
|
info: &ServerInfo,
|
||||||
reader: &mut TokioBufReader<TokioTcpStream>,
|
reader: &mut TokioBufReader<TokioTcpStream>,
|
||||||
) -> Result<User> {
|
) -> Result<User, ListenerError> {
|
||||||
let mut buffer = String::new();
|
let mut buffer = String::new();
|
||||||
|
|
||||||
let mut writer = TokioBufWriter::new(TokioTcpStream::from_std(stream.try_clone()?)?);
|
let mut writer = TokioBufWriter::new(TokioTcpStream::from_std(stream.try_clone()?)?);
|
||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
match reader.read_line(&mut buffer).await {
|
match reader.read_line(&mut buffer).await {
|
||||||
Ok(0) => bail!("invalid response"),
|
Ok(0) => return Err(ListenerError::ConnectionError),
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let mut conneted_users = CONNECTED_USERS.lock().await;
|
let mut conneted_users = CONNECTED_USERS.lock().await;
|
||||||
let _ = conneted_users.remove(&state.clone().unwrap_all());
|
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,
|
user_wrapped: &User,
|
||||||
receiver: &mut Receiver<Message>,
|
receiver: &mut Receiver<Message>,
|
||||||
writer: &mut TokioBufWriter<TokioTcpStream>,
|
writer: &mut TokioBufWriter<TokioTcpStream>,
|
||||||
) -> Result<()> {
|
) -> Result<(), ListenerError> {
|
||||||
if !user_wrapped.is_populated() {
|
if !user_wrapped.is_populated() {
|
||||||
bail!("user has not registered yet, returning...");
|
return Err(ListenerError::UserIsUnidentified);
|
||||||
}
|
}
|
||||||
|
|
||||||
let user = user_wrapped.clone().unwrap_all();
|
let user = user_wrapped.clone().unwrap_all();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
use anyhow::Result;
|
|
||||||
use tokio::{
|
use tokio::{
|
||||||
io::{AsyncWriteExt, BufWriter},
|
io::{AsyncWriteExt, BufWriter},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::error_structs::SenderError;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IrcResponse {
|
pub struct IrcResponse {
|
||||||
pub sender: Option<String>,
|
pub sender: Option<String>,
|
||||||
|
|
@ -32,7 +33,7 @@ impl IrcResponse {
|
||||||
hostname: &str,
|
hostname: &str,
|
||||||
writer: &mut BufWriter<TcpStream>,
|
writer: &mut BufWriter<TcpStream>,
|
||||||
prepend_column: bool,
|
prepend_column: bool,
|
||||||
) -> Result<()> {
|
) -> Result<(), SenderError> {
|
||||||
let sender = format!(":{}", self.sender.clone().unwrap_or(hostname.to_string()));
|
let sender = format!(":{}", self.sender.clone().unwrap_or(hostname.to_string()));
|
||||||
let mut full_response = Vec::new();
|
let mut full_response = Vec::new();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue