feat: config file support

This commit is contained in:
user0-07161 2025-10-07 21:12:59 +02:00
parent 594e376e21
commit 69fe2d00bc
6 changed files with 141 additions and 15 deletions

56
Cargo.lock generated
View file

@ -617,8 +617,10 @@ dependencies = [
"clap", "clap",
"console-subscriber", "console-subscriber",
"once_cell", "once_cell",
"serde",
"thiserror", "thiserror",
"tokio", "tokio",
"toml",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
] ]
@ -1017,6 +1019,15 @@ dependencies = [
"serde_core", "serde_core",
] ]
[[package]]
name = "serde_spanned"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee"
dependencies = [
"serde_core",
]
[[package]] [[package]]
name = "sharded-slab" name = "sharded-slab"
version = "0.1.7" version = "0.1.7"
@ -1175,6 +1186,45 @@ dependencies = [
"tokio", "tokio",
] ]
[[package]]
name = "toml"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0"
dependencies = [
"indexmap 2.11.4",
"serde_core",
"serde_spanned",
"toml_datetime",
"toml_parser",
"toml_writer",
"winnow",
]
[[package]]
name = "toml_datetime"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1"
dependencies = [
"serde_core",
]
[[package]]
name = "toml_parser"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627"
dependencies = [
"winnow",
]
[[package]]
name = "toml_writer"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109"
[[package]] [[package]]
name = "tonic" name = "tonic"
version = "0.12.3" version = "0.12.3"
@ -1513,6 +1563,12 @@ version = "0.53.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
[[package]]
name = "winnow"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf"
[[package]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.27" version = "0.8.27"

View file

@ -13,6 +13,8 @@ tracing = "0.1.41"
tracing-subscriber = "0.3.20" tracing-subscriber = "0.3.20"
thiserror = "2.0.17" thiserror = "2.0.17"
anyhow = "1.0.100" anyhow = "1.0.100"
toml = "0.9.7"
serde = { version = "1.0.228", features = ["derive"] }
[features] [features]
tokio-console = ["tokio/tracing", "console-subscriber"] tokio-console = ["tokio/tracing", "console-subscriber"]

View file

@ -0,0 +1,5 @@
ip = "0.0.0.0"
port = 6667
server_hostname = "irc.foo.bar"
network_name = "MyCoolFooNet" # this SHOULDN'T HAVE SPACES!
operators = []

52
src/config.rs Normal file
View file

@ -0,0 +1,52 @@
use std::{env::home_dir, fs::read_to_string, path::PathBuf};
use crate::error_structs::ConfigReadError;
use serde::Deserialize;
#[allow(dead_code)]
#[derive(Clone, Debug, Deserialize)]
pub struct ServerInfo {
pub ip: String,
pub port: u64,
pub server_hostname: String,
pub network_name: String,
pub operators: Vec<String>,
}
fn get_config_path() -> Result<PathBuf, ConfigReadError> {
if cfg!(target_os = "linux") {
if let Some(mut homedir) = home_dir() {
homedir.push(".config");
homedir.push("irs");
homedir.push("config.toml");
if homedir.exists() {
return Ok(homedir);
}
}
let dir = PathBuf::from("/etc/irs/config.toml");
if dir.exists() {
dir
} else {
return Err(ConfigReadError::NoConfigFile);
}
} else {
return Err(ConfigReadError::UnsupportedOS);
};
unreachable!()
}
impl ServerInfo {
pub fn load(path: Option<String>) -> Result<Self, ConfigReadError> {
let path = if let Some(path) = path {
PathBuf::from(path)
} else {
get_config_path()?
};
let config: ServerInfo = toml::from_str(&read_to_string(path)?)?;
Ok(config)
}
}

View file

@ -27,6 +27,21 @@ pub enum CommandExecError {
NonexistantCommand, NonexistantCommand,
} }
#[derive(Error, Debug)]
pub enum ConfigReadError {
#[error("could not find a config file")]
NoConfigFile,
#[error("unsupported OS")]
UnsupportedOS,
#[error("std::io error")]
StdIoError(#[from] std::io::Error),
#[error("toml reading error")]
TomlError(#[from] toml::de::Error),
}
// Conversion impls here // Conversion impls here
impl From<SenderError> for ListenerError { impl From<SenderError> for ListenerError {
fn from(value: SenderError) -> Self { fn from(value: SenderError) -> Self {

View file

@ -6,6 +6,7 @@ use std::{
}; };
use anyhow::Error as AnyhowError; use anyhow::Error as AnyhowError;
use clap::Parser;
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},
@ -21,6 +22,7 @@ use tracing::instrument;
use crate::{ use crate::{
channels::Channel, channels::Channel,
config::ServerInfo,
error_structs::{HandlerError, ListenerError}, error_structs::{HandlerError, ListenerError},
login::send_motd, login::send_motd,
messages::Message, messages::Message,
@ -30,6 +32,7 @@ use crate::{
mod channels; mod channels;
mod commands; mod commands;
mod config;
mod error_structs; mod error_structs;
mod login; mod login;
mod messages; mod messages;
@ -42,14 +45,12 @@ pub static JOINED_CHANNELS: Lazy<Mutex<HashSet<Channel>>> =
Lazy::new(|| Mutex::new(HashSet::new())); Lazy::new(|| Mutex::new(HashSet::new()));
pub static SENDER: Lazy<Mutex<Option<Sender<Message>>>> = Lazy::new(|| Mutex::new(None)); pub static SENDER: Lazy<Mutex<Option<Sender<Message>>>> = Lazy::new(|| Mutex::new(None));
#[allow(dead_code)] /// An IRCd written in Rust
#[derive(Clone, Debug)] #[derive(Parser, Debug)]
struct ServerInfo { struct Args {
ip: String, /// Path to the config file
port: String, #[arg(short, long)]
server_hostname: String, pub config_path: Option<String>,
network_name: String,
operators: Vec<String>,
} }
#[tokio::main] #[tokio::main]
@ -57,13 +58,8 @@ async fn main() -> Result<(), AnyhowError> {
#[cfg(feature = "tokio-console")] #[cfg(feature = "tokio-console")]
console_subscriber::init(); console_subscriber::init();
let info = ServerInfo { let args = Args::parse();
ip: "0.0.0.0".into(), let info = ServerInfo::load(args.config_path).unwrap();
port: "6667".into(),
server_hostname: "irc.blah.blah".into(),
network_name: "TeamDunno".into(),
operators: Vec::new(),
};
// TODO: ^ pull these from a config file // TODO: ^ pull these from a config file
let listener = TcpListener::bind(SocketAddr::from_str(&format!("{}:{}", info.ip, info.port))?)?; let listener = TcpListener::bind(SocketAddr::from_str(&format!("{}:{}", info.ip, info.port))?)?;