use log; use std::collections::HashMap; use tokio::sync::Mutex; use tokio::fs; use tauri::{AppHandle, Manager, State}; use crate::model::misc::Game; use crate::pkg::{Package, PkgKey}; use crate::pkg_store::{InstallResult, PackageStore}; use crate::profiles::ongeki::OngekiProfile; use crate::profiles::{self, AnyProfile, Profile, ProfileMeta, ProfilePaths}; use crate::appdata::AppData; use crate::model::misc::StartCheckError; use crate::util; #[tauri::command] pub async fn start_check(state: State<'_, Mutex>) -> Result, String> { log::debug!("invoke: start_check"); let appd = state.lock().await; let prf = appd.profile.as_ref().ok_or_else(|| format!("No profile to check"))?; let pkgs = appd.pkgs.get_all(); let mut res = Vec::new(); for key in prf.mod_pkgs() { if let Some(pkg) = pkgs.get(key) { if let Some(loc) = &pkg.loc { for dep in &loc.dependencies { if !prf.mod_pkgs().contains(dep) { res.push(StartCheckError::MissingDependency(key.clone(), dep.clone())); } } } else { res.push(StartCheckError::MissingLocalPackage(key.clone())); } } else { res.push(StartCheckError::MissingRemotePackage(key.clone())); } } for key in prf.special_pkgs() { log::debug!("special: {}", key); if let Some(pkg) = pkgs.get(&key) { if pkg.loc.is_some() { continue; } } res.push(StartCheckError::MissingTool(key)); } Ok(res) } #[tauri::command] pub async fn startline(app: AppHandle) -> Result<(), String> { log::debug!("invoke: startline"); let state = app.state::>(); let mut hash = "".to_owned(); let mut appd = state.lock().await; if let Some(p) = &appd.profile { hash = appd.sum_packages(p); } if let Some(p) = &mut appd.profile { log::debug!("{}", hash); p.line_up(hash, app.clone()).await .map_err(|e| format!("Lineup failed:\n{}", e))?; p.start(app.clone()).await .map_err(|e| format!("Startup failed:\n{}", e))?; Ok(()) } else { Err("No profile".to_owned()) } } #[tauri::command] pub async fn kill() -> Result<(), String> { util::pkill("amdaemon.exe").await; // The start routine will kill the other process Ok(()) } #[tauri::command] pub async fn install_package( state: State<'_, tokio::sync::Mutex>, key: PkgKey, force: bool ) -> Result { log::debug!("invoke: install_package({})", key); let mut appd = state.lock().await; appd.pkgs.install_package(&key, force, true) .await .map_err(|e| e.to_string()) } #[tauri::command] pub async fn delete_package(state: State<'_, tokio::sync::Mutex>, key: PkgKey) -> Result<(), String> { log::debug!("invoke: delete_package({})", key); let mut appd = state.lock().await; appd.pkgs.delete_package(&key, true) .await .map_err(|e| e.to_string()) } #[tauri::command] pub async fn get_package(state: State<'_, tokio::sync::Mutex>, key: PkgKey) -> Result { log::debug!("invoke: get_package({})", key); let appd = state.lock().await; appd.pkgs.get(&key) .map_err(|e| e.to_string()) .cloned() } #[tauri::command] pub async fn toggle_package(state: State<'_, tokio::sync::Mutex>, key: PkgKey, enable: bool) -> Result<(), String> { log::debug!("invoke: toggle_package({}, {})", key, enable); let mut appd = state.lock().await; appd.toggle_package(key, enable) .map_err(|e| e.to_string()) } #[tauri::command] pub async fn reload_all_packages(state: State<'_, tokio::sync::Mutex>) -> Result<(), String> { log::debug!("invoke: reload_all_packages"); let mut appd = state.lock().await; appd.pkgs.reload_all() .await .map_err(|e| e.to_string()) } #[tauri::command] pub async fn get_all_packages(state: State<'_, Mutex>) -> Result, ()> { log::debug!("invoke: get_all_packages"); let appd = state.lock().await; Ok(appd.pkgs.get_all()) } #[tauri::command] pub async fn fetch_listings(state: State<'_, Mutex>) -> Result<(), String> { log::debug!("invoke: fetch_listings"); { let appd = state.lock().await; if !appd.pkgs.is_offline() { log::warn!("fetch_listings: already done"); return Ok(()); } if appd.cfg.offline_mode { log::info!("fetch_listings: skipped"); return Err("offline mode".to_owned()); } } let listings = PackageStore::fetch_listings().await .map_err(|e| e.to_string())?; let mut appd = state.lock().await; appd.pkgs.process_fetched_listings(listings); Ok(()) } #[tauri::command] pub async fn list_profiles() -> Result, String> { log::debug!("invoke: list_profiles"); let list = crate::profiles::list_profiles().await.map_err(|e| e.to_string())?; Ok(list) } #[tauri::command] pub async fn init_profile( state: State<'_, Mutex>, game: Game, name: String ) -> Result<(), String> { log::debug!("invoke: init_profile({}, {})", game, name); let mut appd = state.lock().await; let new_profile = OngekiProfile::new(name) .map_err(|e| format!("Unable to create profile: {}", e))?; fs::create_dir_all(new_profile.config_dir()).await .map_err(|e| format!("Unable to create the profile config directory: {}", e))?; fs::create_dir_all(new_profile.data_dir()).await .map_err(|e| format!("Unable to create the profile data directory: {}", e))?; appd.profile = Some(AnyProfile::OngekiProfile(new_profile.clone())); Ok(()) } #[tauri::command] pub async fn load_profile(state: State<'_, Mutex>, game: Game, name: String) -> Result<(), String> { log::debug!("invoke: load_profile({} {:?})", game, name); let mut appd = state.lock().await; appd.switch_profile(game, name).map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub async fn rename_profile( state: State<'_, Mutex>, profile: ProfileMeta, name: String ) -> Result<(), String> { log::debug!("invoke: rename_profile({:?} {:?})", profile, name); let new_meta = ProfileMeta { game: profile.game.clone(), name: profiles::fixed_name(&ProfileMeta { game: profile.game.clone(), name }, false) }; if new_meta.name == profile.name { return Ok(()); } if new_meta.config_dir().exists() { return Err(format!("Profile {} already exists", &new_meta.name)); } fs::rename(profile.config_dir(), new_meta.config_dir()).await .map_err(|e| format!("Unable to rename: {}", e))?; if let Err(e) = fs::rename(profile.data_dir(), new_meta.data_dir()).await { log::warn!("Unable to move data dir {}->{}: {}", &profile.name, &new_meta.name, e); } let mut appd = state.lock().await; if let Some(current) = &mut appd.profile { if current.meta() == profile { current.rename(new_meta.name); } } Ok(()) } #[tauri::command] pub async fn duplicate_profile(profile: ProfileMeta) -> Result<(), String> { log::debug!("invoke: duplicate_profile({:?})", profile); let new_meta = ProfileMeta { game: profile.game.clone(), name: profiles::fixed_name(&profile, true) }; util::copy_directory(profile.config_dir(), new_meta.config_dir(), false) .map_err(|e| format!("Unable to duplicate: {}", e))?; Ok(()) } #[tauri::command] pub async fn delete_profile(state: State<'_, Mutex>, profile: ProfileMeta) -> Result<(), String> { log::debug!("invoke: delete_profile({:?})", profile); std::fs::remove_dir_all(profile.config_dir()) .map_err(|e| format!("Unable to delete {:?}: {}", profile.config_dir(), e))?; if let Err(e) = std::fs::remove_dir_all(profile.data_dir()) { log::warn!("Unable to delete: {:?} {}", profile.data_dir(), e); } let mut appd = state.lock().await; if let Some(current) = &mut appd.profile { if current.meta() == profile { appd.profile = None; } } Ok(()) } #[tauri::command] pub async fn get_current_profile(state: State<'_, Mutex>) -> Result, ()> { log::debug!("invoke: get_current_profile"); let appd = state.lock().await; Ok(appd.profile.clone()) } #[tauri::command] pub async fn save_current_profile(state: State<'_, Mutex>, profile: AnyProfile) -> Result<(), String> { log::debug!("invoke: save_current_profile"); let mut appd = state.lock().await; profile.save().map_err(|e| e.to_string())?; appd.profile = Some(profile); Ok(()) } #[tauri::command] pub async fn list_platform_capabilities() -> Result, ()> { log::debug!("invoke: list_platform_capabilities"); #[cfg(target_os = "windows")] return Ok(vec!["display".to_owned()]); #[cfg(target_os = "linux")] return Ok(vec!["wine".to_owned()]); } #[tauri::command] #[cfg(target_os = "windows")] pub async fn list_displays() -> Result, String> { use winsafe::prelude::NativeBitflag; log::debug!("invoke: list_displays"); let mut res = Vec::new(); for displ_dev in winsafe::EnumDisplayDevices(None, None) { if let Ok(displ_dev) = displ_dev { if displ_dev.StateFlags.has(winsafe::co::DISPLAY_DEVICE::ATTACHED_TO_DESKTOP) { res.push((displ_dev.DeviceName(), displ_dev.DeviceString())); } } else { break; } } Ok(res) } #[tauri::command] #[cfg(not(target_os = "windows"))] pub async fn list_displays() -> Result, ()> { log::debug!("invoke: list_displays"); Ok(Vec::new()) } #[tauri::command] pub async fn list_directories() -> Result { log::debug!("invoke: list_directores"); Ok(util::all_dirs().clone()) }