//! Theseus profile management interface use std::path::PathBuf; use io::IOError; use tokio::sync::RwLock; use crate::{ event::emit::{emit_loading, init_loading}, prelude::DirectoryInfo, state::{self, Profiles}, util::io, }; pub use crate::{ state::{ Hooks, JavaSettings, MemorySettings, Profile, Settings, WindowSize, }, State, }; /// Gets entire settings #[tracing::instrument] pub async fn get() -> crate::Result { let state = State::get().await?; let settings = state.settings.read().await; Ok(settings.clone()) } /// Sets entire settings #[tracing::instrument] pub async fn set(settings: Settings) -> crate::Result<()> { let state = State::get().await?; if settings.loaded_config_dir != state.settings.read().await.loaded_config_dir { return Err(crate::ErrorKind::OtherError( "Cannot change config directory as setting".to_string(), ) .as_error()); } let (reset_io, reset_fetch) = async { let read = state.settings.read().await; ( settings.max_concurrent_writes != read.max_concurrent_writes, settings.max_concurrent_downloads != read.max_concurrent_downloads, ) } .await; { *state.settings.write().await = settings; } if reset_io { state.reset_io_semaphore().await; } if reset_fetch { state.reset_fetch_semaphore().await; } State::sync().await?; Ok(()) } /// Sets the new config dir, the location of all Theseus data except for the settings.json and caches /// Takes control of the entire state and blocks until completion pub async fn set_config_dir(new_config_dir: PathBuf) -> crate::Result<()> { if !new_config_dir.is_dir() { return Err(crate::ErrorKind::FSError(format!( "New config dir is not a folder: {}", new_config_dir.display() )) .as_error()); } let loading_bar = init_loading( crate::LoadingBarType::ConfigChange { new_path: new_config_dir.clone(), }, 100.0, "Changing configuration directory", ) .await?; tracing::trace!("Changing config dir, taking control of the state"); // Take control of the state let mut state_write = State::get_write().await?; let old_config_dir = state_write.directories.config_dir.read().await.clone(); tracing::trace!("Setting configuration setting"); // Set load config dir setting let settings = { let mut settings = state_write.settings.write().await; settings.loaded_config_dir = Some(new_config_dir.clone()); // Some java paths are hardcoded to within our config dir, so we need to update them tracing::trace!("Updating java keys"); for key in settings.java_globals.keys() { if let Some(java) = settings.java_globals.get_mut(&key) { // If the path is within the old config dir path, update it to the new config dir if let Ok(relative_path) = PathBuf::from(java.path.clone()) .strip_prefix(&old_config_dir) { java.path = new_config_dir .join(relative_path) .to_string_lossy() .to_string(); } } } tracing::trace!("Syncing settings"); settings .sync(&state_write.directories.settings_file()) .await?; settings.clone() }; tracing::trace!("Reinitializing directory"); // Set new state information state_write.directories = DirectoryInfo::init(&settings)?; let total_entries = std::fs::read_dir(&old_config_dir) .map_err(|e| IOError::with_path(e, &old_config_dir))? .count() as f64; // Move all files over from state_write.directories.config_dir to new_config_dir tracing::trace!("Renaming folder structure"); let mut i = 0.0; let mut entries = io::read_dir(&old_config_dir).await?; while let Some(entry) = entries .next_entry() .await .map_err(|e| IOError::with_path(e, &old_config_dir))? { let entry_path = entry.path(); if let Some(file_name) = entry_path.file_name() { // Ignore settings.json if file_name == state::SETTINGS_FILE_NAME { continue; } // Ignore caches folder if file_name == state::CACHES_FOLDER_NAME { continue; } // Ignore modrinth_logs folder if file_name == state::LAUNCHER_LOGS_FOLDER_NAME { continue; } let new_path = new_config_dir.join(file_name); io::rename(entry_path, new_path).await?; i += 1.0; emit_loading(&loading_bar, 90.0 * (i / total_entries), None) .await?; } } // Reset file watcher tracing::trace!("Reset file watcher"); let mut file_watcher = state::init_watcher().await?; // Reset profiles (for filepaths, file watcher, etc) state_write.profiles = RwLock::new( Profiles::init(&state_write.directories, &mut file_watcher).await?, ); state_write.file_watcher = RwLock::new(file_watcher); emit_loading(&loading_bar, 10.0, None).await?; // TODO: need to be able to safely error out of this function, reverting the changes tracing::info!( "Successfully switched config folder to: {}", new_config_dir.display() ); Ok(()) }