forked from akanyan/STARTLINER
392 lines
11 KiB
Rust
392 lines
11 KiB
Rust
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::{self, Profile, ProfileData, ProfileMeta, ProfilePaths};
|
|
use crate::appdata::{AppData, ToggleAction};
|
|
use crate::model::misc::StartCheckError;
|
|
use crate::util;
|
|
|
|
#[tauri::command]
|
|
pub async fn start_check(state: State<'_, Mutex<AppData>>) -> Result<Vec<StartCheckError>, 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, refresh: bool) -> Result<(), String> {
|
|
log::debug!("invoke: startline");
|
|
|
|
let state = app.state::<Mutex<AppData>>();
|
|
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, refresh, app.clone()).await
|
|
.map_err(|e| format!("Lineup failed:\n{}", e))?;
|
|
let app_clone = app.clone();
|
|
let p_clone = p.clone();
|
|
tauri::async_runtime::spawn(async move {
|
|
if let Err(e) = p_clone.start(app_clone).await {
|
|
log::error!("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<AppData>>,
|
|
key: PkgKey,
|
|
force: bool
|
|
) -> Result<InstallResult, String> {
|
|
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<AppData>>, 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())?;
|
|
|
|
appd.toggle_package(key, ToggleAction::Disable)
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn get_package(state: State<'_, tokio::sync::Mutex<AppData>>, key: PkgKey) -> Result<Package, String> {
|
|
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<AppData>>, key: PkgKey, enable: bool) -> Result<(), String> {
|
|
log::debug!("invoke: toggle_package({}, {})", key, enable);
|
|
|
|
let mut appd = state.lock().await;
|
|
appd.toggle_package(key, if enable { ToggleAction::EnableRecursive } else { ToggleAction::Disable })
|
|
.map_err(|e| e.to_string())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn reload_all_packages(state: State<'_, tokio::sync::Mutex<AppData>>) -> 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<AppData>>) -> Result<HashMap<PkgKey, Package>, ()> {
|
|
log::debug!("invoke: get_all_packages");
|
|
|
|
let appd = state.lock().await;
|
|
|
|
Ok(appd.pkgs.get_all())
|
|
}
|
|
|
|
|
|
#[tauri::command]
|
|
pub async fn get_game_packages(state: State<'_, Mutex<AppData>>, game: Game) -> Result<Vec<PkgKey>, ()> {
|
|
log::debug!("invoke: get_game_packages {game}");
|
|
|
|
let appd = state.lock().await;
|
|
|
|
Ok(appd.pkgs.get_game_list(game))
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn fetch_listings(state: State<'_, Mutex<AppData>>) -> Result<(), String> {
|
|
log::debug!("invoke: fetch_listings");
|
|
|
|
// let game;
|
|
{
|
|
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());
|
|
}
|
|
// if let Some(profile) = &appd.profile {
|
|
// game = profile.meta.game;
|
|
// } else {
|
|
// return Err("No profile".to_owned());
|
|
// }
|
|
}
|
|
|
|
// Can be this lazy for now as there are only two short lists
|
|
let listings1 = PackageStore::fetch_listings(Game::Ongeki).await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
let listings2 = PackageStore::fetch_listings(Game::Chunithm).await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
let mut appd = state.lock().await;
|
|
appd.pkgs.process_fetched_listings(listings1, Game::Ongeki);
|
|
appd.pkgs.process_fetched_listings(listings2, Game::Chunithm);
|
|
|
|
appd.pkgs.save().await
|
|
.map_err(|e| e.to_string())?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn list_profiles() -> Result<Vec<ProfileMeta>, 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<AppData>>,
|
|
game: Game,
|
|
name: String
|
|
) -> Result<(), String> {
|
|
log::debug!("invoke: init_profile({}, {})", game, name);
|
|
|
|
let mut appd = state.lock().await;
|
|
let new_profile = Profile::new(ProfileMeta { game, name })
|
|
.map_err(|e| format!("Unable to create profile: {}", e))?;
|
|
|
|
appd.profile = Some(new_profile);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn load_profile(state: State<'_, Mutex<AppData>>, 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())?;
|
|
appd.fix();
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn rename_profile(
|
|
state: State<'_, Mutex<AppData>>,
|
|
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<AppData>>, 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<AppData>>) -> Result<Option<Profile>, ()> {
|
|
log::debug!("invoke: get_current_profile");
|
|
|
|
let appd = state.lock().await;
|
|
Ok(appd.profile.clone())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn sync_current_profile(state: State<'_, Mutex<AppData>>, data: ProfileData) -> Result<(), String> {
|
|
log::debug!("invoke: sync_current_profile");
|
|
|
|
let mut appd = state.lock().await;
|
|
if let Some(p) = &mut appd.profile {
|
|
p.sync(data);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn save_current_profile(state: State<'_, Mutex<AppData>>) -> Result<(), String> {
|
|
log::debug!("invoke: save_current_profile");
|
|
|
|
let mut appd = state.lock().await;
|
|
appd.fix();
|
|
match &mut appd.profile {
|
|
Some(p) => {
|
|
p.save().map_err(|e| e.to_string())
|
|
},
|
|
None => {
|
|
Err("no profile to save".to_owned())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn list_platform_capabilities() -> Result<Vec<String>, ()> {
|
|
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<Vec<(String, String)>, 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<Vec<String>, ()> {
|
|
log::debug!("invoke: list_displays");
|
|
|
|
Ok(Vec::new())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn list_directories() -> Result<util::Dirs, ()> {
|
|
log::debug!("invoke: list_directores");
|
|
|
|
Ok(util::all_dirs().clone())
|
|
} |