Skip to content

Commit

Permalink
Add config file (#14)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Kitt3120 committed Dec 29, 2023
1 parent 7031a0f commit 6d67db0
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 1 deletion.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ repository = "https://github.com/Kitt3120/lum"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
dirs = "5.0.1"
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
sqlx = { version = "0.7.3", features = ["runtime-tokio", "any", "postgres", "mysql", "sqlite", "tls-native-tls", "migrate", "macros", "uuid", "chrono", "json"] }
thiserror = "1.0.52"
tokio = { version = "1.35.1", features = ["full"] }
123 changes: 123 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use core::fmt;
use std::{
fmt::{Display, Formatter},
fs, io,
path::PathBuf,
};

use serde::{Deserialize, Serialize};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum ConfigPathError {
#[error("Unable to get OS config directory")]
UnknownBasePath,
}

#[derive(Debug, Error)]
pub enum ConfigInitError {
#[error("Unable to get config path: {0}")]
Path(#[from] ConfigPathError),
#[error("I/O error: {0}")]
IO(#[from] io::Error),
}

#[derive(Debug, Error)]
pub enum ConfigParseError {
#[error("Unable to get config path: {0}")]
Path(#[from] ConfigPathError),
#[error("Unable to initialize config: {0}")]
Init(#[from] ConfigInitError),
#[error("Unable to serialize or deserialize config: {0}")]
Serde(#[from] serde_json::Error),
#[error("I/O error: {0}")]
IO(#[from] io::Error),
}

fn discord_token_default() -> String {
String::from("Please provide a token")
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct Config {
#[serde(rename = "discordToken", default = "discord_token_default")]
pub discord_token: String,
}

impl Default for Config {
fn default() -> Self {
Config {
discord_token: discord_token_default(),
}
}
}

impl Display for Config {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "discord_token: {}", self.discord_token)
}
}

#[derive(Debug)]
pub struct ConfigHandler {
pub app_name: String,
}

impl ConfigHandler {
pub fn new(app_name: &str) -> Self {
ConfigHandler {
app_name: app_name.to_string(),
}
}

pub fn get_config_dir_path(&self) -> Result<PathBuf, ConfigPathError> {
let mut path = match dirs::config_dir() {
Some(path) => path,
None => return Err(ConfigPathError::UnknownBasePath),
};

path.push(&self.app_name);
Ok(path)
}

pub fn create_config_dir_path(&self) -> Result<(), ConfigInitError> {
let path = self.get_config_dir_path()?;
std::fs::create_dir_all(path)?;
Ok(())
}

pub fn get_config_file_path(&self) -> Result<PathBuf, ConfigPathError> {
let mut path = self.get_config_dir_path()?;
path.push("config.json");
Ok(path)
}

pub fn save_config(&self, config: &Config) -> Result<(), ConfigParseError> {
let path = self.get_config_file_path()?;

if !path.exists() {
self.create_config_dir_path()?;
}

let config_json = serde_json::to_string_pretty(config)?;

fs::write(path, config_json)?;

Ok(())
}

pub fn get_config(&self) -> Result<Config, ConfigParseError> {
let path = self.get_config_file_path()?;
if !path.exists() {
self.create_config_dir_path()?;
fs::write(&path, "{}")?;
}

let config_json = fs::read_to_string(path)?;
let config: Config = serde_json::from_str(&config_json)?;

self.save_config(&config)?; // In case the config file was missing some fields which serde used the defaults for

Ok(config)
}
}
14 changes: 13 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
mod config;

pub const BOT_NAME: &str = "Lum";

fn main() {
println!("Hello, world!");
let config_handler = config::ConfigHandler::new(BOT_NAME.to_lowercase().as_str());
let config = match config_handler.get_config() {
Ok(config) => config,
Err(err) => {
panic!("Error reading config file: {}", err);
}
};

println!("Config: {}", config);
}

0 comments on commit 6d67db0

Please sign in to comment.