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/platform/paths.rs | |
| 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 '')
| -rw-r--r-- | src/platform/paths.rs | 270 |
1 files changed, 247 insertions, 23 deletions
diff --git a/src/platform/paths.rs b/src/platform/paths.rs index b430f09..07313fd 100644 --- a/src/platform/paths.rs +++ b/src/platform/paths.rs | |||
| @@ -1,44 +1,268 @@ | |||
| 1 | use std::{fs::create_dir_all, path::PathBuf}; | 1 | use std::{fs::create_dir_all, path::PathBuf}; |
| 2 | 2 | ||
| 3 | use crate::{config::Config, errors::McError}; | 3 | use crate::{config::Config, constants::directory, errors::McError}; |
| 4 | 4 | ||
| 5 | /// ~/.local/share/dml/minecraft | 5 | /// Returns the path to the root Minecraft directory inside the launcher data folder. |
| 6 | pub fn minecraft_root(cfg: &Config) -> PathBuf { | 6 | /// |
| 7 | /// This directory acts as the root for all Minecraft-related files within the launcher data. | ||
| 8 | /// By default, it joins the `minecraft` directory under the data folder (defined by the launcher). | ||
| 9 | /// | ||
| 10 | /// The returned path does not include any specific assets, versions, or libraries directories. | ||
| 11 | /// You can use this as the base path to construct other Minecraft-related paths. | ||
| 12 | /// | ||
| 13 | /// # Parameters | ||
| 14 | /// - `cfg`: The configuration object (`Config`) containing launcher and Minecraft data folder paths. | ||
| 15 | /// | ||
| 16 | /// # Returns | ||
| 17 | /// - A `PathBuf` pointing to the root Minecraft directory. | ||
| 18 | pub fn root_directory(cfg: &Config) -> PathBuf { | ||
| 19 | // Remove DEFAULT_LAUNCHER_DIR to avoid duplicate "dml/dml" | ||
| 7 | cfg.data_dir.join("minecraft") | 20 | cfg.data_dir.join("minecraft") |
| 8 | } | 21 | } |
| 9 | 22 | ||
| 10 | pub fn assets_dir(cfg: &Config) -> PathBuf { | 23 | /// Returns the path to the Minecraft assets directory. |
| 11 | minecraft_root(cfg).join("assets") | 24 | /// |
| 25 | /// This is where Minecraft assets such as textures, sounds, and other resources are stored. | ||
| 26 | /// The assets directory is located inside the root Minecraft directory and can be used | ||
| 27 | /// for accessing game assets. | ||
| 28 | /// | ||
| 29 | /// # Parameters | ||
| 30 | /// - `cfg`: The configuration object (`Config`) that contains paths for the launcher and Minecraft data. | ||
| 31 | /// | ||
| 32 | /// # Returns | ||
| 33 | /// - A `PathBuf` pointing to the assets directory inside the root Minecraft directory. | ||
| 34 | pub fn assets_directory(cfg: &Config) -> PathBuf { | ||
| 35 | root_directory(cfg).join(directory::ASSETS) | ||
| 12 | } | 36 | } |
| 13 | 37 | ||
| 14 | pub fn game_dir(cfg: &Config) -> PathBuf { minecraft_root(cfg) } | 38 | /// Returns the path to the game directory (same as root directory for now). |
| 15 | pub fn ensure_dirs(cfg: &Config) -> Result<(), McError> { | 39 | /// |
| 16 | let root = minecraft_root(cfg); | 40 | /// The game directory currently points to the same location as the root directory. |
| 17 | create_dir_all(&root)?; | 41 | /// In future iterations, this could be adjusted if game-specific files need to be handled separately. |
| 18 | create_dir_all(root.join("versions"))?; | 42 | /// |
| 19 | create_dir_all(root.join("libraries"))?; | 43 | /// # Parameters |
| 20 | create_dir_all(assets_dir(cfg))?; | 44 | /// - `cfg`: The configuration object (`Config`) that holds the paths to the launcher and Minecraft data. |
| 21 | create_dir_all(assets_dir(cfg).join("indexes"))?; | 45 | /// |
| 22 | create_dir_all(assets_dir(cfg).join("objects"))?; | 46 | /// # Returns |
| 23 | create_dir_all(root.join("saves"))?; | 47 | /// - A `PathBuf` pointing to the game directory, which is the same as the root Minecraft directory for now. |
| 48 | pub fn game_directory(cfg: &Config) -> PathBuf { | ||
| 49 | root_directory(cfg) | ||
| 50 | } | ||
| 51 | |||
| 52 | /// Ensures that all necessary directories for Minecraft are created. | ||
| 53 | /// | ||
| 54 | /// This function checks if essential directories for Minecraft's file structure exist and creates them | ||
| 55 | /// if they do not. This includes directories for versions, libraries, assets, and saves. | ||
| 56 | /// | ||
| 57 | /// # Parameters | ||
| 58 | /// - `cfg`: The configuration object (`Config`) that contains paths for the Minecraft data and the required directories. | ||
| 59 | /// | ||
| 60 | /// # Returns | ||
| 61 | /// - `Ok(())` if all directories are created successfully. | ||
| 62 | /// - `Err(McError)` if an error occurs while creating any directory (such as permission issues). | ||
| 63 | /// | ||
| 64 | /// # Example | ||
| 65 | /// ```rust | ||
| 66 | /// let cfg = Config { /* configuration values */ }; | ||
| 67 | /// ensure_directories(&cfg)?; | ||
| 68 | /// ``` | ||
| 69 | /// | ||
| 70 | /// # Notes | ||
| 71 | /// The function creates several directories under the root Minecraft directory, including: | ||
| 72 | /// - Root directory | ||
| 73 | /// - Versions | ||
| 74 | /// - Libraries | ||
| 75 | /// - Assets (with subdirectories for `indexes` and `objects`) | ||
| 76 | /// - Saves | ||
| 77 | /// Each of these directories is essential for managing Minecraft game data and resources. | ||
| 78 | pub fn ensure_directories(cfg: &Config) -> Result<(), McError> { | ||
| 79 | let root: PathBuf = root_directory(cfg); | ||
| 80 | |||
| 81 | for dir in [ | ||
| 82 | root.clone(), | ||
| 83 | root.join(directory::VERSIONS), | ||
| 84 | root.join(directory::LIBRARIES), | ||
| 85 | assets_directory(cfg), | ||
| 86 | assets_directory(cfg).join(directory::INDEXES), | ||
| 87 | assets_directory(cfg).join(directory::OBJECTS), | ||
| 88 | root.join(directory::SAVES), | ||
| 89 | ] { | ||
| 90 | create_dir_all(dir)?; | ||
| 91 | } | ||
| 24 | 92 | ||
| 25 | Ok(()) | 93 | Ok(()) |
| 26 | } | 94 | } |
| 27 | 95 | ||
| 96 | /// Returns the path to a specific version directory inside the root Minecraft directory. | ||
| 97 | /// | ||
| 98 | /// This directory is where the game version-specific files (such as the `.jar` file) are stored. | ||
| 99 | /// The path is constructed by joining the `versions` directory with the provided version name. | ||
| 100 | /// | ||
| 101 | /// # Parameters | ||
| 102 | /// - `cfg`: The configuration object (`Config`) containing paths for the Minecraft data. | ||
| 103 | /// - `version`: The version of Minecraft (e.g., "1.18.2") to generate the path for. | ||
| 104 | /// | ||
| 105 | /// # Returns | ||
| 106 | /// - A `PathBuf` pointing to the directory for the specified version inside the root Minecraft directory. | ||
| 28 | pub fn version_dir(cfg: &Config, version: &str) -> PathBuf { | 107 | pub fn version_dir(cfg: &Config, version: &str) -> PathBuf { |
| 29 | minecraft_root(cfg).join("versions").join(version) | 108 | root_directory(cfg) |
| 109 | .join(directory::VERSIONS) | ||
| 110 | .join(version) | ||
| 30 | } | 111 | } |
| 31 | 112 | ||
| 32 | pub fn client_jar(cfg: &Config, version: &str) -> Result<PathBuf, McError> { | 113 | /// Returns the path to the client `.jar` file for a specific Minecraft version. |
| 33 | Ok(version_dir(cfg, version).join(format!("{version}.jar"))) | 114 | /// |
| 115 | /// This file is typically used to run the Minecraft client for a particular version. | ||
| 116 | /// The path is constructed by joining the version directory with the name of the `.jar` file. | ||
| 117 | /// | ||
| 118 | /// # Parameters | ||
| 119 | /// - `cfg`: The configuration object (`Config`) containing paths for the Minecraft data. | ||
| 120 | /// - `version`: The version of Minecraft (e.g., "1.18.2") to generate the path for. | ||
| 121 | /// | ||
| 122 | /// # Returns | ||
| 123 | /// - A `PathBuf` pointing to the `.jar` file for the specified version in the version directory. | ||
| 124 | pub fn client_jar(cfg: &Config, version: &str) -> PathBuf { | ||
| 125 | version_dir(cfg, version).join(format!("{version}.jar")) | ||
| 34 | } | 126 | } |
| 35 | 127 | ||
| 36 | pub fn library_file(cfg: &Config, rel_path: &str) -> Result<PathBuf, McError> { | 128 | /// Returns the path to a specific library file inside the library directory. |
| 37 | Ok(minecraft_root(cfg) | 129 | /// |
| 38 | .join("libraries") | 130 | /// This function is used to access library files required for running Minecraft. The `rel_path` |
| 39 | .join(rel_path)) | 131 | /// argument specifies the relative path inside the `libraries` directory. It is useful for handling |
| 132 | /// dependencies or loading required libraries based on the game version. | ||
| 133 | /// | ||
| 134 | /// # Parameters | ||
| 135 | /// - `cfg`: The configuration object (`Config`) that contains paths for the Minecraft data. | ||
| 136 | /// - `rel_path`: The relative path of the library file inside the `libraries` directory. | ||
| 137 | /// | ||
| 138 | /// # Returns | ||
| 139 | /// - A `PathBuf` pointing to the specified library file inside the `libraries` directory. | ||
| 140 | pub fn library_file(cfg: &Config, rel_path: &str) -> PathBuf { | ||
| 141 | root_directory(cfg) | ||
| 142 | .join(directory::LIBRARIES) | ||
| 143 | .join(rel_path) | ||
| 40 | } | 144 | } |
| 41 | 145 | ||
| 146 | /// Returns the path to the natives directory for a specific Minecraft version. | ||
| 147 | /// | ||
| 148 | /// The natives directory contains platform-specific native libraries (e.g., `.dll`, `.so`, `.dylib`) | ||
| 149 | /// that Minecraft uses to run the game. This function returns the path to the directory for the | ||
| 150 | /// specified version. | ||
| 151 | /// | ||
| 152 | /// # Parameters | ||
| 153 | /// - `cfg`: The configuration object (`Config`) containing paths for the Minecraft data. | ||
| 154 | /// - `version`: The version of Minecraft (e.g., "1.18.2") to generate the path for. | ||
| 155 | /// | ||
| 156 | /// # Returns | ||
| 157 | /// - A `PathBuf` pointing to the natives directory for the specified version. | ||
| 42 | pub fn natives_dir(cfg: &Config, version: &str) -> PathBuf { | 158 | pub fn natives_dir(cfg: &Config, version: &str) -> PathBuf { |
| 43 | version_dir(cfg, version).join("natives") | 159 | version_dir(cfg, version).join(directory::NATIVES) |
| 160 | } | ||
| 161 | |||
| 162 | // /// Path to a version’s saves directory. | ||
| 163 | // pub fn saves_dir(cfg: &Config) -> PathBuf { | ||
| 164 | // minecraft_root(cfg).join(dirs::SAVES) | ||
| 165 | // } | ||
| 166 | |||
| 167 | #[cfg(test)] | ||
| 168 | mod tests { | ||
| 169 | use super::*; | ||
| 170 | use std::path::Path; | ||
| 171 | |||
| 172 | fn test_config(tmp: &Path) -> Config { | ||
| 173 | Config { | ||
| 174 | data_dir: tmp.to_path_buf(), | ||
| 175 | ..Default::default() | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | #[test] | ||
| 180 | fn root_directory_is_correct() { | ||
| 181 | let tmp = tempfile::tempdir().unwrap(); | ||
| 182 | let cfg = test_config(tmp.path()); | ||
| 183 | |||
| 184 | let root = root_directory(&cfg); | ||
| 185 | assert_eq!(root, tmp.path().join("minecraft")); | ||
| 186 | } | ||
| 187 | |||
| 188 | #[test] | ||
| 189 | fn assets_directory_is_correct() { | ||
| 190 | let tmp = tempfile::tempdir().unwrap(); | ||
| 191 | let cfg = test_config(tmp.path()); | ||
| 192 | |||
| 193 | let assets = assets_directory(&cfg); | ||
| 194 | assert_eq!(assets, root_directory(&cfg).join(directory::ASSETS)); | ||
| 195 | } | ||
| 196 | |||
| 197 | #[test] | ||
| 198 | fn version_paths_are_correct() { | ||
| 199 | let tmp = tempfile::tempdir().unwrap(); | ||
| 200 | let cfg = test_config(tmp.path()); | ||
| 201 | |||
| 202 | let version = "1.20.1"; | ||
| 203 | assert_eq!( | ||
| 204 | version_dir(&cfg, version), | ||
| 205 | root_directory(&cfg) | ||
| 206 | .join(directory::VERSIONS) | ||
| 207 | .join(version) | ||
| 208 | ); | ||
| 209 | |||
| 210 | assert_eq!( | ||
| 211 | client_jar(&cfg, version), | ||
| 212 | version_dir(&cfg, version).join("1.20.1.jar") | ||
| 213 | ); | ||
| 214 | } | ||
| 215 | |||
| 216 | #[test] | ||
| 217 | fn library_file_path_is_correct() { | ||
| 218 | let tmp = tempfile::tempdir().unwrap(); | ||
| 219 | let cfg = test_config(tmp.path()); | ||
| 220 | |||
| 221 | let rel = "com/example/lib.jar"; | ||
| 222 | assert_eq!( | ||
| 223 | library_file(&cfg, rel), | ||
| 224 | root_directory(&cfg) | ||
| 225 | .join(directory::LIBRARIES) | ||
| 226 | .join(rel) | ||
| 227 | ); | ||
| 228 | } | ||
| 229 | |||
| 230 | #[test] | ||
| 231 | fn natives_dir_path_is_correct() { | ||
| 232 | let tmp = tempfile::tempdir().unwrap(); | ||
| 233 | let cfg = test_config(tmp.path()); | ||
| 234 | |||
| 235 | let version = "1.20.1"; | ||
| 236 | assert_eq!( | ||
| 237 | natives_dir(&cfg, version), | ||
| 238 | version_dir(&cfg, version).join(directory::NATIVES) | ||
| 239 | ); | ||
| 240 | } | ||
| 241 | |||
| 242 | #[test] | ||
| 243 | fn ensure_directories_creates_structure() { | ||
| 244 | let tmp = tempfile::tempdir().unwrap(); | ||
| 245 | let cfg = test_config(tmp.path()); | ||
| 246 | |||
| 247 | ensure_directories(&cfg).unwrap(); | ||
| 248 | |||
| 249 | let root = root_directory(&cfg); | ||
| 250 | |||
| 251 | let expected_dirs = [ | ||
| 252 | root.clone(), | ||
| 253 | root.join(directory::VERSIONS), | ||
| 254 | root.join(directory::LIBRARIES), | ||
| 255 | root.join(directory::ASSETS), | ||
| 256 | root.join(directory::ASSETS) | ||
| 257 | .join(directory::INDEXES), | ||
| 258 | root.join(directory::ASSETS) | ||
| 259 | .join(directory::OBJECTS), | ||
| 260 | root.join(directory::SAVES), | ||
| 261 | ]; | ||
| 262 | |||
| 263 | for dir in expected_dirs { | ||
| 264 | assert!(dir.exists(), "Missing directory: {:?}", dir); | ||
| 265 | assert!(dir.is_dir()); | ||
| 266 | } | ||
| 267 | } | ||
| 44 | } | 268 | } |
