diff --git a/Cargo.toml b/Cargo.toml index d39ef20..ee0b2a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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"] } diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..7a17995 --- /dev/null +++ b/src/config.rs @@ -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 { + 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 { + 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 { + 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) + } +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..992058b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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); }