From f7b4b643ebc52a4d72d90d9adbdddc9aa0721e4a Mon Sep 17 00:00:00 2001 From: Filip Wandzio Date: Wed, 25 Feb 2026 16:10:23 +0100 Subject: Feat: Refactor core download logic with concurrency and async features Implement basic unit testing Implement automatic java executable switching based on game version Split loader module into smaller modules Implement basic documentation --- src/config/loader.rs | 129 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 77 insertions(+), 52 deletions(-) (limited to 'src/config/loader.rs') diff --git a/src/config/loader.rs b/src/config/loader.rs index d4b142e..47514e5 100644 --- a/src/config/loader.rs +++ b/src/config/loader.rs @@ -1,65 +1,90 @@ -use std::{env::var, fs::read_to_string, path::PathBuf}; - +use std::{env, fs::read_to_string, path::PathBuf}; use directories::ProjectDirs; -use serde::Deserialize; +use uuid::Uuid; +use super::{file::FileConfig, runtime::RuntimeConfig}; use crate::{constants::*, errors::McError}; -#[allow(dead_code)] -#[derive(Debug, Deserialize)] -pub struct Config { - pub username: String, - pub uuid: String, - pub version: String, - pub java_path: String, - pub max_memory_mb: u32, - pub data_dir: PathBuf, - pub cache_dir: PathBuf, - pub config_dir: PathBuf, - #[serde(default)] - pub jvm_args: Vec, -} -impl Config { - pub fn load() -> Result { - let cfg_path = default_config_path()?; - let mut cfg: Config = if cfg_path.exists() { - let txt = read_to_string(&cfg_path)?; - toml::from_str(&txt).map_err(|e| McError::Config(e.to_string()))? + +pub struct ConfigLoader; + +impl ConfigLoader { + pub fn load(cfg_file: Option<&PathBuf>) -> Result { + let path = match cfg_file { + | Some(p) => p.clone(), + | None => Self::default_config_path()?, + }; + + let mut file_cfg = if path.exists() { + Self::read_file(&path)? } else { - Self::default() + Self::default_file_config()? }; - if let Ok(v) = var("MC_USERNAME") { - cfg.username = v; - } - if let Ok(v) = var("MC_VERSION") { - cfg.version = v; - } - if let Ok(v) = var("MC_JAVA_PATH") { - cfg.java_path = v; - } - if let Ok(v) = var("MC_MAX_MEMORY_MB") { - cfg.max_memory_mb = v.parse().unwrap_or(cfg.max_memory_mb); - } - Ok(cfg) + + Self::apply_env_overrides(&mut file_cfg); + + Ok(RuntimeConfig::from_file(file_cfg)) + } + + fn read_file(path: &PathBuf) -> Result { + let content = read_to_string(path).map_err(|e| { + McError::Config(format!( + "Failed to read config file {}: {}", + path.display(), + e + )) + })?; + + toml::from_str(&content).map_err(|e| { + McError::Config(format!("Failed to parse config file: {}", e)) + }) } - fn default() -> Self { - let base = - ProjectDirs::from("com", "example", "dml").expect("platform dirs"); - Self { - username: "Player".into(), - uuid: uuid::Uuid::new_v4().to_string(), + fn apply_env_overrides(cfg: &mut FileConfig) { + cfg.username = env::var(ENV_USERNAME) + .unwrap_or_else(|_| DEFAULT_USERNAME.to_string()); + + cfg.version = env::var(ENV_VERSION) + .unwrap_or_else(|_| DEFAULT_VERSION.to_string()); + + cfg.java_path = env::var(ENV_JAVA_PATH) + .unwrap_or_else(|_| DEFAULT_JAVA_PATH.to_string()); + + cfg.max_memory_mb = env::var(ENV_MAX_MEMORY_MB) + .ok() + .and_then(|v| v.parse().ok()) + .unwrap_or(DEFAULT_MAX_MEMORY_MB); + } + + fn default_config_path() -> Result { + let base = ProjectDirs::from( + DEFAULT_COMPANY, + DEFAULT_PROJECT_GROUP, + DEFAULT_PROJECT_NAME, + ) + .ok_or_else(|| McError::Config(DEFAULT_ERR_PLATFORM_DIR.into()))?; + + Ok(base.config_dir().join(DEFAULT_CONFIG_FILENAME)) + } + + fn default_file_config() -> Result { + let base = ProjectDirs::from( + DEFAULT_COMPANY, + DEFAULT_PROJECT_GROUP, + DEFAULT_PROJECT_NAME, + ) + .ok_or_else(|| McError::Config(DEFAULT_ERR_PLATFORM_DIR.into()))?; + + Ok(FileConfig { + username: DEFAULT_USERNAME.into(), + uuid: Uuid::new_v4().to_string(), version: DEFAULT_VERSION.into(), - java_path: DEFAULT_JAVA_PATH.into(), max_memory_mb: DEFAULT_MAX_MEMORY_MB, + java_path: DEFAULT_JAVA_PATH.into(), data_dir: base.data_dir().into(), - cache_dir: base.cache_dir().into(), - config_dir: base.config_dir().into(), + // cache_dir: base.cache_dir().into(), + // config_dir: base.config_dir().into(), jvm_args: vec![], - } + runtimes: vec![], + }) } } -fn default_config_path() -> Result { - let base = ProjectDirs::from("com", "example", "dml") - .ok_or_else(|| McError::Config("cannot determine config dir".into()))?; - Ok(base.config_dir().join("config.toml")) -} -- cgit v1.2.3