diff options
| author | Filip Wandzio <contact@philw.dev> | 2026-02-25 16:10:23 +0100 |
|---|---|---|
| committer | Filip Wandzio <contact@philw.dev> | 2026-02-25 16:10:23 +0100 |
| commit | f7b4b643ebc52a4d72d90d9adbdddc9aa0721e4a (patch) | |
| tree | c96432be342b02bc0409e5b78b6b5d54afcc7cd6 /src/config | |
| parent | 2e10b0713f5369f489d2ababd70108cc359c5d2d (diff) | |
| download | dml-f7b4b643ebc52a4d72d90d9adbdddc9aa0721e4a.tar.gz dml-f7b4b643ebc52a4d72d90d9adbdddc9aa0721e4a.zip | |
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
Diffstat (limited to 'src/config')
| -rw-r--r-- | src/config/file.rs | 26 | ||||
| -rw-r--r-- | src/config/loader.rs | 129 | ||||
| -rw-r--r-- | src/config/mod.rs | 17 | ||||
| -rw-r--r-- | src/config/runtime.rs | 95 |
4 files changed, 206 insertions, 61 deletions
diff --git a/src/config/file.rs b/src/config/file.rs new file mode 100644 index 0000000..17f2cb2 --- /dev/null +++ b/src/config/file.rs | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | use std::path::PathBuf; | ||
| 2 | |||
| 3 | use serde::Deserialize; | ||
| 4 | |||
| 5 | use crate::minecraft::launcher::JavaRuntime; | ||
| 6 | |||
| 7 | #[derive(Debug, Deserialize)] | ||
| 8 | pub struct FileConfig { | ||
| 9 | pub username: String, | ||
| 10 | pub uuid: String, | ||
| 11 | pub version: String, | ||
| 12 | pub max_memory_mb: u32, | ||
| 13 | |||
| 14 | #[serde(default)] | ||
| 15 | pub jvm_args: Vec<String>, | ||
| 16 | |||
| 17 | #[serde(default)] | ||
| 18 | pub runtimes: Vec<JavaRuntime>, | ||
| 19 | |||
| 20 | #[serde(default)] | ||
| 21 | pub java_path: String, | ||
| 22 | |||
| 23 | pub data_dir: PathBuf, | ||
| 24 | // pub cache_dir: PathBuf, | ||
| 25 | // pub config_dir: PathBuf, | ||
| 26 | } | ||
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 @@ | |||
| 1 | use std::{env::var, fs::read_to_string, path::PathBuf}; | 1 | use std::{env, fs::read_to_string, path::PathBuf}; |
| 2 | |||
| 3 | use directories::ProjectDirs; | 2 | use directories::ProjectDirs; |
| 4 | use serde::Deserialize; | 3 | use uuid::Uuid; |
| 5 | 4 | ||
| 5 | use super::{file::FileConfig, runtime::RuntimeConfig}; | ||
| 6 | use crate::{constants::*, errors::McError}; | 6 | use crate::{constants::*, errors::McError}; |
| 7 | #[allow(dead_code)] | 7 | |
| 8 | #[derive(Debug, Deserialize)] | 8 | pub struct ConfigLoader; |
| 9 | pub struct Config { | 9 | |
| 10 | pub username: String, | 10 | impl ConfigLoader { |
| 11 | pub uuid: String, | 11 | pub fn load(cfg_file: Option<&PathBuf>) -> Result<RuntimeConfig, McError> { |
| 12 | pub version: String, | 12 | let path = match cfg_file { |
| 13 | pub java_path: String, | 13 | | Some(p) => p.clone(), |
| 14 | pub max_memory_mb: u32, | 14 | | None => Self::default_config_path()?, |
| 15 | pub data_dir: PathBuf, | 15 | }; |
| 16 | pub cache_dir: PathBuf, | 16 | |
| 17 | pub config_dir: PathBuf, | 17 | let mut file_cfg = if path.exists() { |
| 18 | #[serde(default)] | 18 | Self::read_file(&path)? |
| 19 | pub jvm_args: Vec<String>, | ||
| 20 | } | ||
| 21 | impl Config { | ||
| 22 | pub fn load() -> Result<Self, McError> { | ||
| 23 | let cfg_path = default_config_path()?; | ||
| 24 | let mut cfg: Config = if cfg_path.exists() { | ||
| 25 | let txt = read_to_string(&cfg_path)?; | ||
| 26 | toml::from_str(&txt).map_err(|e| McError::Config(e.to_string()))? | ||
| 27 | } else { | 19 | } else { |
| 28 | Self::default() | 20 | Self::default_file_config()? |
| 29 | }; | 21 | }; |
| 30 | if let Ok(v) = var("MC_USERNAME") { | 22 | |
| 31 | cfg.username = v; | 23 | Self::apply_env_overrides(&mut file_cfg); |
| 32 | } | 24 | |
| 33 | if let Ok(v) = var("MC_VERSION") { | 25 | Ok(RuntimeConfig::from_file(file_cfg)) |
| 34 | cfg.version = v; | 26 | } |
| 35 | } | 27 | |
| 36 | if let Ok(v) = var("MC_JAVA_PATH") { | 28 | fn read_file(path: &PathBuf) -> Result<FileConfig, McError> { |
| 37 | cfg.java_path = v; | 29 | let content = read_to_string(path).map_err(|e| { |
| 38 | } | 30 | McError::Config(format!( |
| 39 | if let Ok(v) = var("MC_MAX_MEMORY_MB") { | 31 | "Failed to read config file {}: {}", |
| 40 | cfg.max_memory_mb = v.parse().unwrap_or(cfg.max_memory_mb); | 32 | path.display(), |
| 41 | } | 33 | e |
| 42 | Ok(cfg) | 34 | )) |
| 35 | })?; | ||
| 36 | |||
| 37 | toml::from_str(&content).map_err(|e| { | ||
| 38 | McError::Config(format!("Failed to parse config file: {}", e)) | ||
| 39 | }) | ||
| 43 | } | 40 | } |
| 44 | 41 | ||
| 45 | fn default() -> Self { | 42 | fn apply_env_overrides(cfg: &mut FileConfig) { |
| 46 | let base = | 43 | cfg.username = env::var(ENV_USERNAME) |
| 47 | ProjectDirs::from("com", "example", "dml").expect("platform dirs"); | 44 | .unwrap_or_else(|_| DEFAULT_USERNAME.to_string()); |
| 48 | Self { | 45 | |
| 49 | username: "Player".into(), | 46 | cfg.version = env::var(ENV_VERSION) |
| 50 | uuid: uuid::Uuid::new_v4().to_string(), | 47 | .unwrap_or_else(|_| DEFAULT_VERSION.to_string()); |
| 48 | |||
| 49 | cfg.java_path = env::var(ENV_JAVA_PATH) | ||
| 50 | .unwrap_or_else(|_| DEFAULT_JAVA_PATH.to_string()); | ||
| 51 | |||
| 52 | cfg.max_memory_mb = env::var(ENV_MAX_MEMORY_MB) | ||
| 53 | .ok() | ||
| 54 | .and_then(|v| v.parse().ok()) | ||
| 55 | .unwrap_or(DEFAULT_MAX_MEMORY_MB); | ||
| 56 | } | ||
| 57 | |||
| 58 | fn default_config_path() -> Result<PathBuf, McError> { | ||
| 59 | let base = ProjectDirs::from( | ||
| 60 | DEFAULT_COMPANY, | ||
| 61 | DEFAULT_PROJECT_GROUP, | ||
| 62 | DEFAULT_PROJECT_NAME, | ||
| 63 | ) | ||
| 64 | .ok_or_else(|| McError::Config(DEFAULT_ERR_PLATFORM_DIR.into()))?; | ||
| 65 | |||
| 66 | Ok(base.config_dir().join(DEFAULT_CONFIG_FILENAME)) | ||
| 67 | } | ||
| 68 | |||
| 69 | fn default_file_config() -> Result<FileConfig, McError> { | ||
| 70 | let base = ProjectDirs::from( | ||
| 71 | DEFAULT_COMPANY, | ||
| 72 | DEFAULT_PROJECT_GROUP, | ||
| 73 | DEFAULT_PROJECT_NAME, | ||
| 74 | ) | ||
| 75 | .ok_or_else(|| McError::Config(DEFAULT_ERR_PLATFORM_DIR.into()))?; | ||
| 76 | |||
| 77 | Ok(FileConfig { | ||
| 78 | username: DEFAULT_USERNAME.into(), | ||
| 79 | uuid: Uuid::new_v4().to_string(), | ||
| 51 | version: DEFAULT_VERSION.into(), | 80 | version: DEFAULT_VERSION.into(), |
| 52 | java_path: DEFAULT_JAVA_PATH.into(), | ||
| 53 | max_memory_mb: DEFAULT_MAX_MEMORY_MB, | 81 | max_memory_mb: DEFAULT_MAX_MEMORY_MB, |
| 82 | java_path: DEFAULT_JAVA_PATH.into(), | ||
| 54 | data_dir: base.data_dir().into(), | 83 | data_dir: base.data_dir().into(), |
| 55 | cache_dir: base.cache_dir().into(), | 84 | // cache_dir: base.cache_dir().into(), |
| 56 | config_dir: base.config_dir().into(), | 85 | // config_dir: base.config_dir().into(), |
| 57 | jvm_args: vec![], | 86 | jvm_args: vec![], |
| 58 | } | 87 | runtimes: vec![], |
| 88 | }) | ||
| 59 | } | 89 | } |
| 60 | } | 90 | } |
| 61 | fn default_config_path() -> Result<PathBuf, McError> { | ||
| 62 | let base = ProjectDirs::from("com", "example", "dml") | ||
| 63 | .ok_or_else(|| McError::Config("cannot determine config dir".into()))?; | ||
| 64 | Ok(base.config_dir().join("config.toml")) | ||
| 65 | } | ||
diff --git a/src/config/mod.rs b/src/config/mod.rs index 066154a..a375dda 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs | |||
| @@ -1,12 +1,11 @@ | |||
| 1 | //! Configuration module for the DML launcher. | 1 | //! Configuration module for the DML launcher. |
| 2 | //! | ||
| 3 | //! This module contains submodules and helpers for loading, parsing, and | ||
| 4 | //! managing configuration files and settings. It abstracts the details | ||
| 5 | //! of where configuration is stored and how it is represented in memory. | ||
| 6 | //! | ||
| 7 | //! # Submodules | ||
| 8 | //! - `loader`: Functions to load and parse configuration from disk or | ||
| 9 | //! environment variables. | ||
| 10 | 2 | ||
| 3 | pub mod file; | ||
| 11 | pub mod loader; | 4 | pub mod loader; |
| 12 | pub use loader::Config; | 5 | pub mod runtime; |
| 6 | |||
| 7 | pub use loader::ConfigLoader; | ||
| 8 | pub use runtime::RuntimeConfig; | ||
| 9 | |||
| 10 | /// Backwards-compatibility alias so existing code can keep using `Config` | ||
| 11 | pub type Config = RuntimeConfig; | ||
diff --git a/src/config/runtime.rs b/src/config/runtime.rs new file mode 100644 index 0000000..46857f0 --- /dev/null +++ b/src/config/runtime.rs | |||
| @@ -0,0 +1,95 @@ | |||
| 1 | use std::path::PathBuf; | ||
| 2 | |||
| 3 | use super::file::FileConfig; | ||
| 4 | use crate::{constants::*, minecraft::launcher::JavaRuntime}; | ||
| 5 | |||
| 6 | /// Configuration for the runtime environment used to launch Minecraft. | ||
| 7 | /// | ||
| 8 | /// The `RuntimeConfig` struct holds the configuration settings required to | ||
| 9 | /// launch Minecraft with a specific Java runtime. This includes details about | ||
| 10 | /// the user, the Minecraft version, the available Java runtimes, and JVM options. | ||
| 11 | /// | ||
| 12 | /// The `RuntimeConfig` struct is typically used by the launcher to store and | ||
| 13 | /// manage the necessary settings for running the game. It also supports | ||
| 14 | /// dynamic configuration by allowing the addition of Java runtimes and the | ||
| 15 | /// specification of system paths and arguments. | ||
| 16 | #[derive(Debug)] | ||
| 17 | #[derive(Default)] | ||
| 18 | pub struct RuntimeConfig { | ||
| 19 | /// The username of the player running Minecraft. | ||
| 20 | pub username: String, | ||
| 21 | |||
| 22 | /// The UUID (unique identifier) of the user. | ||
| 23 | pub uuid: String, | ||
| 24 | |||
| 25 | /// The version of Minecraft that the user is running. | ||
| 26 | pub version: String, | ||
| 27 | |||
| 28 | /// The maximum amount of memory (in megabytes) allocated to the Java process. | ||
| 29 | /// This setting determines how much memory the Minecraft instance can use. | ||
| 30 | pub max_memory_mb: u32, | ||
| 31 | |||
| 32 | /// A list of arguments to pass to the JVM when launching Minecraft. | ||
| 33 | /// These arguments can be used to customize the JVM's behavior, such as | ||
| 34 | /// memory settings, garbage collection options, etc. | ||
| 35 | pub jvm_args: Vec<String>, | ||
| 36 | |||
| 37 | /// A list of Java runtime environments available for Minecraft. | ||
| 38 | /// If no specific Java runtime is provided, the default one is used. | ||
| 39 | pub runtimes: Vec<JavaRuntime>, | ||
| 40 | |||
| 41 | /// The directory where Minecraft data (e.g., worlds, logs, configs) is stored. | ||
| 42 | /// This path is critical for loading and saving game data. | ||
| 43 | pub data_dir: PathBuf, | ||
| 44 | } | ||
| 45 | |||
| 46 | impl RuntimeConfig { | ||
| 47 | /// Creates a new `RuntimeConfig` instance from a `FileConfig`. | ||
| 48 | /// | ||
| 49 | /// This method takes a `FileConfig` object (typically loaded from a configuration file) | ||
| 50 | /// and constructs a `RuntimeConfig` instance. It ensures that if no runtimes are defined | ||
| 51 | /// but a Java path is provided, a default Java runtime is created based on the specified path. | ||
| 52 | /// | ||
| 53 | /// # Parameters | ||
| 54 | /// - `file`: A `FileConfig` instance containing initial configuration values for the | ||
| 55 | /// Minecraft runtime. This includes user details, Minecraft version, JVM arguments, | ||
| 56 | /// and the path to Java (if specified). | ||
| 57 | /// | ||
| 58 | /// # Returns | ||
| 59 | /// - A `RuntimeConfig` instance initialized with the values from `file`. If no runtimes | ||
| 60 | /// were provided in `file` but a Java path was given, the method will add a default Java runtime. | ||
| 61 | /// | ||
| 62 | /// # Example | ||
| 63 | /// ```rust | ||
| 64 | /// let file_config = FileConfig { | ||
| 65 | /// username: "Player1".into(), | ||
| 66 | /// uuid: "1234-5678-9101".into(), | ||
| 67 | /// version: "1.18.2".into(), | ||
| 68 | /// max_memory_mb: 2048, | ||
| 69 | /// jvm_args: vec!["-Xmx2G".into()], | ||
| 70 | /// runtimes: Vec::new(), | ||
| 71 | /// java_path: "/path/to/java".into(), | ||
| 72 | /// data_dir: "/path/to/minecraft/data".into(), | ||
| 73 | /// }; | ||
| 74 | /// | ||
| 75 | /// let runtime_config = RuntimeConfig::from_file(file_config); | ||
| 76 | /// ``` | ||
| 77 | pub fn from_file(mut file: FileConfig) -> Self { | ||
| 78 | if file.runtimes.is_empty() && !file.java_path.is_empty() { | ||
| 79 | file.runtimes.push(JavaRuntime { | ||
| 80 | major: DEFAULT_JAVA_MAJOR, | ||
| 81 | path: PathBuf::from(&file.java_path), | ||
| 82 | }); | ||
| 83 | } | ||
| 84 | |||
| 85 | Self { | ||
| 86 | username: file.username, | ||
| 87 | uuid: file.uuid, | ||
| 88 | version: file.version, | ||
| 89 | max_memory_mb: file.max_memory_mb, | ||
| 90 | jvm_args: file.jvm_args, | ||
| 91 | runtimes: file.runtimes, | ||
| 92 | data_dir: file.data_dir, | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
