use std::{fs::create_dir_all, path::PathBuf}; use crate::{config::Config, constants::directory, errors::McError}; /// Returns the path to the root Minecraft directory inside the launcher data folder. /// /// This directory acts as the root for all Minecraft-related files within the launcher data. /// By default, it joins the `minecraft` directory under the data folder (defined by the launcher). /// /// The returned path does not include any specific assets, versions, or libraries directories. /// You can use this as the base path to construct other Minecraft-related paths. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) containing launcher and Minecraft data folder paths. /// /// # Returns /// - A `PathBuf` pointing to the root Minecraft directory. pub fn root_directory(cfg: &Config) -> PathBuf { // Remove DEFAULT_LAUNCHER_DIR to avoid duplicate "dml/dml" cfg.data_dir.join("minecraft") } /// Returns the path to the Minecraft assets directory. /// /// This is where Minecraft assets such as textures, sounds, and other resources are stored. /// The assets directory is located inside the root Minecraft directory and can be used /// for accessing game assets. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) that contains paths for the launcher and Minecraft data. /// /// # Returns /// - A `PathBuf` pointing to the assets directory inside the root Minecraft directory. pub fn assets_directory(cfg: &Config) -> PathBuf { root_directory(cfg).join(directory::ASSETS) } /// Returns the path to the game directory (same as root directory for now). /// /// The game directory currently points to the same location as the root directory. /// In future iterations, this could be adjusted if game-specific files need to be handled separately. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) that holds the paths to the launcher and Minecraft data. /// /// # Returns /// - A `PathBuf` pointing to the game directory, which is the same as the root Minecraft directory for now. pub fn game_directory(cfg: &Config) -> PathBuf { root_directory(cfg) } /// Ensures that all necessary directories for Minecraft are created. /// /// This function checks if essential directories for Minecraft's file structure exist and creates them /// if they do not. This includes directories for versions, libraries, assets, and saves. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) that contains paths for the Minecraft data and the required directories. /// /// # Returns /// - `Ok(())` if all directories are created successfully. /// - `Err(McError)` if an error occurs while creating any directory (such as permission issues). /// /// # Example /// ```rust /// let cfg = Config { /* configuration values */ }; /// ensure_directories(&cfg)?; /// ``` /// /// # Notes /// The function creates several directories under the root Minecraft directory, including: /// - Root directory /// - Versions /// - Libraries /// - Assets (with subdirectories for `indexes` and `objects`) /// - Saves /// Each of these directories is essential for managing Minecraft game data and resources. pub fn ensure_directories(cfg: &Config) -> Result<(), McError> { let root: PathBuf = root_directory(cfg); for dir in [ root.clone(), root.join(directory::VERSIONS), root.join(directory::LIBRARIES), assets_directory(cfg), assets_directory(cfg).join(directory::INDEXES), assets_directory(cfg).join(directory::OBJECTS), root.join(directory::SAVES), ] { create_dir_all(dir)?; } Ok(()) } /// Returns the path to a specific version directory inside the root Minecraft directory. /// /// This directory is where the game version-specific files (such as the `.jar` file) are stored. /// The path is constructed by joining the `versions` directory with the provided version name. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) containing paths for the Minecraft data. /// - `version`: The version of Minecraft (e.g., "1.18.2") to generate the path for. /// /// # Returns /// - A `PathBuf` pointing to the directory for the specified version inside the root Minecraft directory. pub fn version_dir(cfg: &Config, version: &str) -> PathBuf { root_directory(cfg) .join(directory::VERSIONS) .join(version) } /// Returns the path to the client `.jar` file for a specific Minecraft version. /// /// This file is typically used to run the Minecraft client for a particular version. /// The path is constructed by joining the version directory with the name of the `.jar` file. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) containing paths for the Minecraft data. /// - `version`: The version of Minecraft (e.g., "1.18.2") to generate the path for. /// /// # Returns /// - A `PathBuf` pointing to the `.jar` file for the specified version in the version directory. pub fn client_jar(cfg: &Config, version: &str) -> PathBuf { version_dir(cfg, version).join(format!("{version}.jar")) } /// Returns the path to a specific library file inside the library directory. /// /// This function is used to access library files required for running Minecraft. The `rel_path` /// argument specifies the relative path inside the `libraries` directory. It is useful for handling /// dependencies or loading required libraries based on the game version. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) that contains paths for the Minecraft data. /// - `rel_path`: The relative path of the library file inside the `libraries` directory. /// /// # Returns /// - A `PathBuf` pointing to the specified library file inside the `libraries` directory. pub fn library_file(cfg: &Config, rel_path: &str) -> PathBuf { root_directory(cfg) .join(directory::LIBRARIES) .join(rel_path) } /// Returns the path to the natives directory for a specific Minecraft version. /// /// The natives directory contains platform-specific native libraries (e.g., `.dll`, `.so`, `.dylib`) /// that Minecraft uses to run the game. This function returns the path to the directory for the /// specified version. /// /// # Parameters /// - `cfg`: The configuration object (`Config`) containing paths for the Minecraft data. /// - `version`: The version of Minecraft (e.g., "1.18.2") to generate the path for. /// /// # Returns /// - A `PathBuf` pointing to the natives directory for the specified version. pub fn natives_dir(cfg: &Config, version: &str) -> PathBuf { version_dir(cfg, version).join(directory::NATIVES) } // /// Path to a version’s saves directory. // pub fn saves_dir(cfg: &Config) -> PathBuf { // minecraft_root(cfg).join(dirs::SAVES) // } #[cfg(test)] mod tests { use super::*; use std::path::Path; fn test_config(tmp: &Path) -> Config { Config { data_dir: tmp.to_path_buf(), ..Default::default() } } #[test] fn root_directory_is_correct() { let tmp = tempfile::tempdir().unwrap(); let cfg = test_config(tmp.path()); let root = root_directory(&cfg); assert_eq!(root, tmp.path().join("minecraft")); } #[test] fn assets_directory_is_correct() { let tmp = tempfile::tempdir().unwrap(); let cfg = test_config(tmp.path()); let assets = assets_directory(&cfg); assert_eq!(assets, root_directory(&cfg).join(directory::ASSETS)); } #[test] fn version_paths_are_correct() { let tmp = tempfile::tempdir().unwrap(); let cfg = test_config(tmp.path()); let version = "1.20.1"; assert_eq!( version_dir(&cfg, version), root_directory(&cfg) .join(directory::VERSIONS) .join(version) ); assert_eq!( client_jar(&cfg, version), version_dir(&cfg, version).join("1.20.1.jar") ); } #[test] fn library_file_path_is_correct() { let tmp = tempfile::tempdir().unwrap(); let cfg = test_config(tmp.path()); let rel = "com/example/lib.jar"; assert_eq!( library_file(&cfg, rel), root_directory(&cfg) .join(directory::LIBRARIES) .join(rel) ); } #[test] fn natives_dir_path_is_correct() { let tmp = tempfile::tempdir().unwrap(); let cfg = test_config(tmp.path()); let version = "1.20.1"; assert_eq!( natives_dir(&cfg, version), version_dir(&cfg, version).join(directory::NATIVES) ); } #[test] fn ensure_directories_creates_structure() { let tmp = tempfile::tempdir().unwrap(); let cfg = test_config(tmp.path()); ensure_directories(&cfg).unwrap(); let root = root_directory(&cfg); let expected_dirs = [ root.clone(), root.join(directory::VERSIONS), root.join(directory::LIBRARIES), root.join(directory::ASSETS), root.join(directory::ASSETS) .join(directory::INDEXES), root.join(directory::ASSETS) .join(directory::OBJECTS), root.join(directory::SAVES), ]; for dir in expected_dirs { assert!(dir.exists(), "Missing directory: {:?}", dir); assert!(dir.is_dir()); } } }