Skip to content

Commit 6d67db0

Browse files
authored
Add config file (#14)
* Implement config - Add dirs crate - Implement ConfigHandler - Implement custom error types - Implement config struct - Load config on startup * Use thiserror - Add thiserror crate - Refactor existing error types to use thiserror
1 parent 7031a0f commit 6d67db0

File tree

3 files changed

+138
-1
lines changed

3 files changed

+138
-1
lines changed

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ repository = "https://github.com/Kitt3120/lum"
1212
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1313

1414
[dependencies]
15+
dirs = "5.0.1"
1516
serde = { version = "1.0.193", features = ["derive"] }
1617
serde_json = "1.0.108"
1718
sqlx = { version = "0.7.3", features = ["runtime-tokio", "any", "postgres", "mysql", "sqlite", "tls-native-tls", "migrate", "macros", "uuid", "chrono", "json"] }
19+
thiserror = "1.0.52"
1820
tokio = { version = "1.35.1", features = ["full"] }

src/config.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use core::fmt;
2+
use std::{
3+
fmt::{Display, Formatter},
4+
fs, io,
5+
path::PathBuf,
6+
};
7+
8+
use serde::{Deserialize, Serialize};
9+
use thiserror::Error;
10+
11+
#[derive(Debug, Error)]
12+
pub enum ConfigPathError {
13+
#[error("Unable to get OS config directory")]
14+
UnknownBasePath,
15+
}
16+
17+
#[derive(Debug, Error)]
18+
pub enum ConfigInitError {
19+
#[error("Unable to get config path: {0}")]
20+
Path(#[from] ConfigPathError),
21+
#[error("I/O error: {0}")]
22+
IO(#[from] io::Error),
23+
}
24+
25+
#[derive(Debug, Error)]
26+
pub enum ConfigParseError {
27+
#[error("Unable to get config path: {0}")]
28+
Path(#[from] ConfigPathError),
29+
#[error("Unable to initialize config: {0}")]
30+
Init(#[from] ConfigInitError),
31+
#[error("Unable to serialize or deserialize config: {0}")]
32+
Serde(#[from] serde_json::Error),
33+
#[error("I/O error: {0}")]
34+
IO(#[from] io::Error),
35+
}
36+
37+
fn discord_token_default() -> String {
38+
String::from("Please provide a token")
39+
}
40+
41+
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
42+
pub struct Config {
43+
#[serde(rename = "discordToken", default = "discord_token_default")]
44+
pub discord_token: String,
45+
}
46+
47+
impl Default for Config {
48+
fn default() -> Self {
49+
Config {
50+
discord_token: discord_token_default(),
51+
}
52+
}
53+
}
54+
55+
impl Display for Config {
56+
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
57+
write!(f, "discord_token: {}", self.discord_token)
58+
}
59+
}
60+
61+
#[derive(Debug)]
62+
pub struct ConfigHandler {
63+
pub app_name: String,
64+
}
65+
66+
impl ConfigHandler {
67+
pub fn new(app_name: &str) -> Self {
68+
ConfigHandler {
69+
app_name: app_name.to_string(),
70+
}
71+
}
72+
73+
pub fn get_config_dir_path(&self) -> Result<PathBuf, ConfigPathError> {
74+
let mut path = match dirs::config_dir() {
75+
Some(path) => path,
76+
None => return Err(ConfigPathError::UnknownBasePath),
77+
};
78+
79+
path.push(&self.app_name);
80+
Ok(path)
81+
}
82+
83+
pub fn create_config_dir_path(&self) -> Result<(), ConfigInitError> {
84+
let path = self.get_config_dir_path()?;
85+
std::fs::create_dir_all(path)?;
86+
Ok(())
87+
}
88+
89+
pub fn get_config_file_path(&self) -> Result<PathBuf, ConfigPathError> {
90+
let mut path = self.get_config_dir_path()?;
91+
path.push("config.json");
92+
Ok(path)
93+
}
94+
95+
pub fn save_config(&self, config: &Config) -> Result<(), ConfigParseError> {
96+
let path = self.get_config_file_path()?;
97+
98+
if !path.exists() {
99+
self.create_config_dir_path()?;
100+
}
101+
102+
let config_json = serde_json::to_string_pretty(config)?;
103+
104+
fs::write(path, config_json)?;
105+
106+
Ok(())
107+
}
108+
109+
pub fn get_config(&self) -> Result<Config, ConfigParseError> {
110+
let path = self.get_config_file_path()?;
111+
if !path.exists() {
112+
self.create_config_dir_path()?;
113+
fs::write(&path, "{}")?;
114+
}
115+
116+
let config_json = fs::read_to_string(path)?;
117+
let config: Config = serde_json::from_str(&config_json)?;
118+
119+
self.save_config(&config)?; // In case the config file was missing some fields which serde used the defaults for
120+
121+
Ok(config)
122+
}
123+
}

src/main.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
mod config;
2+
3+
pub const BOT_NAME: &str = "Lum";
4+
15
fn main() {
2-
println!("Hello, world!");
6+
let config_handler = config::ConfigHandler::new(BOT_NAME.to_lowercase().as_str());
7+
let config = match config_handler.get_config() {
8+
Ok(config) => config,
9+
Err(err) => {
10+
panic!("Error reading config file: {}", err);
11+
}
12+
};
13+
14+
println!("Config: {}", config);
315
}

0 commit comments

Comments
 (0)