From 7c9dca0ae21a0754ffb9e9899e4dda53d97dd776 Mon Sep 17 00:00:00 2001 From: Schrottkatze Date: Mon, 15 Jan 2024 10:43:35 +0100 Subject: [PATCH] app: improve config loading and merging --- crates/app/src/config.rs | 49 +++++++++++++++++-- crates/app/src/config/cli.rs | 2 +- crates/app/src/config/config_file.rs | 71 ++++++++++------------------ crates/app/src/main.rs | 5 +- 4 files changed, 73 insertions(+), 54 deletions(-) diff --git a/crates/app/src/config.rs b/crates/app/src/config.rs index c19b94a..a0196fe 100644 --- a/crates/app/src/config.rs +++ b/crates/app/src/config.rs @@ -1,6 +1,10 @@ use clap::Parser; -use self::{cli::Args, config_file::Configs}; +use self::{ + cli::Args, + config_file::{find_config_file, Configs}, + error::ConfigError, +}; mod cli; mod config_file; @@ -11,14 +15,49 @@ pub struct Config { } impl Config { - pub fn read() -> Self { + pub fn read() -> Result { let args = Args::parse(); - let cfg = Configs::read(args.config_file); + let config_path = if let Some(config_path) = args.config_path { + config_path + } else { + find_config_file()? + }; + let file_config = Configs::read(config_path)?; - Self { + Ok(Self { // this is negated because to an outward api, the negative is more intuitive, // while in the source the other way around is more intuitive - startup_msg: !(args.no_startup_message || cfg.no_startup_message), + startup_msg: !(args.no_startup_message || file_config.no_startup_message), + }) + } +} + +pub mod error { + #[derive(Debug)] + pub enum ConfigError { + NoConfigDir, + NoConfigFileFound, + IoError(std::io::Error), + UnknownExtension(Option), + SerdeJsonError(serde_json::Error), + SerdeRonError(ron::error::SpannedError), + } + + impl From for ConfigError { + fn from(value: std::io::Error) -> Self { + Self::IoError(value) + } + } + + impl From for ConfigError { + fn from(value: serde_json::Error) -> Self { + Self::SerdeJsonError(value) + } + } + + impl From for ConfigError { + fn from(value: ron::error::SpannedError) -> Self { + Self::SerdeRonError(value) } } } diff --git a/crates/app/src/config/cli.rs b/crates/app/src/config/cli.rs index 30754c5..23f8ef1 100644 --- a/crates/app/src/config/cli.rs +++ b/crates/app/src/config/cli.rs @@ -6,7 +6,7 @@ use clap::Parser; pub(crate) struct Args { /// Read this config file. #[arg(short, long)] - pub config_file: Option, + pub config_path: Option, #[arg(long, env = "NO_STARTUP_MESSAGE", default_value = "false")] pub no_startup_message: bool, } diff --git a/crates/app/src/config/config_file.rs b/crates/app/src/config/config_file.rs index fc96675..d454223 100644 --- a/crates/app/src/config/config_file.rs +++ b/crates/app/src/config/config_file.rs @@ -1,12 +1,11 @@ use std::{ fs, path::{Path, PathBuf}, - process, }; use serde::{Deserialize, Serialize}; -use crate::error_reporting::{report_serde_json_err, report_serde_ron_err}; +use super::error::ConfigError; #[derive(Debug, Serialize, Deserialize)] pub struct Configs { @@ -17,59 +16,37 @@ pub struct Configs { } /// what the fuck serde why do i need this -pub(crate) fn default_example_value() -> i32 { +fn default_example_value() -> i32 { 43 } -pub(crate) fn default_no_startup_msg() -> bool { +fn default_no_startup_msg() -> bool { false } +pub(super) fn find_config_file() -> Result { + let Some(config_path) = dirs::config_dir() else { + return Err(ConfigError::NoConfigDir); + }; + + let ron_path = config_path.with_file_name("config.ron"); + let json_path = config_path.with_file_name("config.json"); + + if Path::new(&ron_path).exists() { + Ok(ron_path) + } else if Path::new(&json_path).exists() { + Ok(json_path) + } else { + Err(ConfigError::NoConfigFileFound) + } +} + impl Configs { - pub fn read(custom_path: Option) -> Self { - use owo_colors::OwoColorize; - let p = match custom_path { - Some(p) => p, - None => { - let config_path = dirs::config_dir().expect("config dir should exist"); - - let ron_path = config_path.with_file_name("config.ron"); - let json_path = config_path.with_file_name("config.json"); - - if Path::new(&ron_path).exists() { - ron_path - } else if Path::new(&json_path).exists() { - json_path - } else { - eprintln!("{}: couldn't find config file", "Fatal error".red()); - process::exit(1) - } - } - }; - + pub fn read(p: PathBuf) -> Result { match p.extension().map(|v| v.to_str().unwrap()) { - Some("ron") => Self::read_json(&fs::read_to_string(p).unwrap()), - Some("json") => Self::read_ron(&fs::read_to_string(p).unwrap()), - None | Some(_) => { - eprintln!( - "{}: couldn't determine config file type", - "Fatal error".red() - ); - process::exit(1) - } - } - } - pub fn read_json(config_text: &str) -> Configs { - match serde_json::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_json_err(config_text, e), - } - } - - pub fn read_ron(config_text: &str) -> Configs { - match ron::from_str(config_text) { - Ok(c) => c, - Err(e) => report_serde_ron_err(config_text, e), + Some("ron") => Ok(serde_json::from_str(&fs::read_to_string(p)?)?), + Some("json") => Ok(ron::from_str(&fs::read_to_string(p)?)?), + e => Err(ConfigError::UnknownExtension(e.map(|v| v.to_owned()))), } } } diff --git a/crates/app/src/main.rs b/crates/app/src/main.rs index deab5b4..dd7f3e4 100644 --- a/crates/app/src/main.rs +++ b/crates/app/src/main.rs @@ -2,11 +2,14 @@ use config::Config; use welcome_msg::print_startup_msg; mod config; + +#[allow(unused)] mod error_reporting; mod welcome_msg; fn main() { - let cfg = Config::read(); + // TODO: proper error handling + let cfg = Config::read().unwrap(); if cfg.startup_msg { print_startup_msg();