Skip to content

Commit 69fe2d0

Browse files
author
user0-07161
committed
feat: config file support
1 parent 594e376 commit 69fe2d0

File tree

6 files changed

+141
-15
lines changed

6 files changed

+141
-15
lines changed

Cargo.lock

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ tracing = "0.1.41"
1313
tracing-subscriber = "0.3.20"
1414
thiserror = "2.0.17"
1515
anyhow = "1.0.100"
16+
toml = "0.9.7"
17+
serde = { version = "1.0.228", features = ["derive"] }
1618

1719
[features]
1820
tokio-console = ["tokio/tracing", "console-subscriber"]

examples/example_config.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
ip = "0.0.0.0"
2+
port = 6667
3+
server_hostname = "irc.foo.bar"
4+
network_name = "MyCoolFooNet" # this SHOULDN'T HAVE SPACES!
5+
operators = []

src/config.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use std::{env::home_dir, fs::read_to_string, path::PathBuf};
2+
3+
use crate::error_structs::ConfigReadError;
4+
use serde::Deserialize;
5+
6+
#[allow(dead_code)]
7+
#[derive(Clone, Debug, Deserialize)]
8+
pub struct ServerInfo {
9+
pub ip: String,
10+
pub port: u64,
11+
pub server_hostname: String,
12+
pub network_name: String,
13+
pub operators: Vec<String>,
14+
}
15+
16+
fn get_config_path() -> Result<PathBuf, ConfigReadError> {
17+
if cfg!(target_os = "linux") {
18+
if let Some(mut homedir) = home_dir() {
19+
homedir.push(".config");
20+
homedir.push("irs");
21+
homedir.push("config.toml");
22+
23+
if homedir.exists() {
24+
return Ok(homedir);
25+
}
26+
}
27+
28+
let dir = PathBuf::from("/etc/irs/config.toml");
29+
if dir.exists() {
30+
dir
31+
} else {
32+
return Err(ConfigReadError::NoConfigFile);
33+
}
34+
} else {
35+
return Err(ConfigReadError::UnsupportedOS);
36+
};
37+
38+
unreachable!()
39+
}
40+
41+
impl ServerInfo {
42+
pub fn load(path: Option<String>) -> Result<Self, ConfigReadError> {
43+
let path = if let Some(path) = path {
44+
PathBuf::from(path)
45+
} else {
46+
get_config_path()?
47+
};
48+
let config: ServerInfo = toml::from_str(&read_to_string(path)?)?;
49+
50+
Ok(config)
51+
}
52+
}

src/error_structs.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ pub enum CommandExecError {
2727
NonexistantCommand,
2828
}
2929

30+
#[derive(Error, Debug)]
31+
pub enum ConfigReadError {
32+
#[error("could not find a config file")]
33+
NoConfigFile,
34+
35+
#[error("unsupported OS")]
36+
UnsupportedOS,
37+
38+
#[error("std::io error")]
39+
StdIoError(#[from] std::io::Error),
40+
41+
#[error("toml reading error")]
42+
TomlError(#[from] toml::de::Error),
43+
}
44+
3045
// Conversion impls here
3146
impl From<SenderError> for ListenerError {
3247
fn from(value: SenderError) -> Self {

src/main.rs

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::{
66
};
77

88
use anyhow::Error as AnyhowError;
9+
use clap::Parser;
910
use once_cell::sync::Lazy;
1011
use tokio::{
1112
io::{AsyncBufReadExt, BufReader as TokioBufReader, BufWriter as TokioBufWriter},
@@ -21,6 +22,7 @@ use tracing::instrument;
2122

2223
use crate::{
2324
channels::Channel,
25+
config::ServerInfo,
2426
error_structs::{HandlerError, ListenerError},
2527
login::send_motd,
2628
messages::Message,
@@ -30,6 +32,7 @@ use crate::{
3032

3133
mod channels;
3234
mod commands;
35+
mod config;
3336
mod error_structs;
3437
mod login;
3538
mod messages;
@@ -42,28 +45,21 @@ pub static JOINED_CHANNELS: Lazy<Mutex<HashSet<Channel>>> =
4245
Lazy::new(|| Mutex::new(HashSet::new()));
4346
pub static SENDER: Lazy<Mutex<Option<Sender<Message>>>> = Lazy::new(|| Mutex::new(None));
4447

45-
#[allow(dead_code)]
46-
#[derive(Clone, Debug)]
47-
struct ServerInfo {
48-
ip: String,
49-
port: String,
50-
server_hostname: String,
51-
network_name: String,
52-
operators: Vec<String>,
48+
/// An IRCd written in Rust
49+
#[derive(Parser, Debug)]
50+
struct Args {
51+
/// Path to the config file
52+
#[arg(short, long)]
53+
pub config_path: Option<String>,
5354
}
5455

5556
#[tokio::main]
5657
async fn main() -> Result<(), AnyhowError> {
5758
#[cfg(feature = "tokio-console")]
5859
console_subscriber::init();
5960

60-
let info = ServerInfo {
61-
ip: "0.0.0.0".into(),
62-
port: "6667".into(),
63-
server_hostname: "irc.blah.blah".into(),
64-
network_name: "TeamDunno".into(),
65-
operators: Vec::new(),
66-
};
61+
let args = Args::parse();
62+
let info = ServerInfo::load(args.config_path).unwrap();
6763
// TODO: ^ pull these from a config file
6864

6965
let listener = TcpListener::bind(SocketAddr::from_str(&format!("{}:{}", info.ip, info.port))?)?;

0 commit comments

Comments
 (0)