Files
ODIAN_Launcher/src-tauri/src/commands.rs
T
Kelorastus 9a3094bebc fix
2026-10-02 11:43:22 +02:00

212 lines
6.4 KiB
Rust

//! Tauri commands: thin wrappers over `Launcher` (see docs/CONTRACT.md).
use tauri::{AppHandle, State};
use tauri_plugin_dialog::DialogExt;
use tauri_plugin_opener::OpenerExt;
use crate::launcher::{Launcher, Market, Status};
use crate::market;
use crate::settings::Settings;
use crate::stack::StackEvent;
use crate::sync::Conflict;
type CmdResult<T> = Result<T, String>;
#[derive(serde::Serialize)]
pub struct SignedIn {
username: String,
}
#[tauri::command]
pub async fn release_plan(launcher: State<'_, Launcher>) -> CmdResult<crate::release::ReleasePlan> {
Ok(launcher.release_plan().await)
}
#[tauri::command]
pub async fn release_run(
launcher: State<'_, Launcher>,
password: String,
with_api: bool,
with_mcp: bool,
with_web: bool,
) -> CmdResult<()> {
launcher.release_run(password, with_api, with_mcp, with_web).await
}
#[tauri::command]
pub async fn refresh_run(
launcher: State<'_, Launcher>,
password: String,
pg_password: String,
tile_root: String,
) -> CmdResult<()> {
launcher.refresh_run(password, pg_password, tile_root).await
}
#[tauri::command]
pub fn release_console(launcher: State<'_, Launcher>, since: usize) -> CmdResult<crate::release::ConsoleTail> {
Ok(launcher.release_console(since))
}
#[tauri::command]
pub async fn get_status(launcher: State<'_, Launcher>) -> CmdResult<Status> {
Ok(launcher.status().await)
}
/// The marketplace: every tile set and source file of the version, required ones first. With
/// `drive`, what that drive can install instead of the DHP.
#[tauri::command]
pub async fn get_market(app: AppHandle, launcher: State<'_, Launcher>, drive: Option<String>) -> CmdResult<Market> {
launcher.market(&app, drive.map(std::path::PathBuf::from)).await
}
/// Choose the optional items (their keys) for an install about to run, without touching what
/// is installed. `drive` names the drive whose items these are.
#[tauri::command]
pub async fn set_market(
app: AppHandle,
launcher: State<'_, Launcher>,
selected: Vec<String>,
drive: Option<String>,
) -> CmdResult<()> {
launcher.choose_market(&app, selected, drive.map(std::path::PathBuf::from)).await
}
/// Choose the optional items (their keys) and apply the choice; see `Launcher::apply_market`.
#[tauri::command]
pub async fn finish_market(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.finish_market(&app).await
}
#[tauri::command]
pub async fn check_market(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Vec<market::Check>> {
launcher.check_market(&app).await
}
#[tauri::command]
pub async fn apply_market(app: AppHandle, launcher: State<'_, Launcher>, selected: Vec<String>) -> CmdResult<()> {
launcher.apply_market(&app, selected).await
}
#[tauri::command]
pub async fn refresh(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Status> {
Ok(launcher.refresh(&app).await)
}
#[tauri::command]
pub async fn sign_in(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<SignedIn> {
launcher.sign_in(&app).await.map(|username| SignedIn { username })
}
#[tauri::command]
pub async fn cancel_sign_in(launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.cancel_sign_in();
Ok(())
}
#[tauri::command]
pub async fn sign_out(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Status> {
Ok(launcher.sign_out(&app).await)
}
#[tauri::command]
pub async fn get_notes(launcher: State<'_, Launcher>, version: Option<String>) -> CmdResult<String> {
launcher.notes(version).await
}
/// Links in patch notes open in the system browser; the launcher window never navigates.
#[tauri::command]
pub async fn open_external(app: AppHandle, url: String) -> CmdResult<()> {
let parsed = url::Url::parse(&url).map_err(|_| "That link is not a valid address.".to_string())?;
if !matches!(parsed.scheme(), "http" | "https") {
return Err("Only web links can be opened.".to_string());
}
app
.opener()
.open_url(parsed.as_str(), None::<&str>)
.map_err(|e| format!("cannot open the browser: {e}"))
}
#[tauri::command]
pub async fn start_install(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.start_install(&app, None).await
}
#[tauri::command]
pub async fn start_install_from_drive(app: AppHandle, launcher: State<'_, Launcher>, path: String) -> CmdResult<()> {
launcher.start_install(&app, Some(std::path::PathBuf::from(path))).await
}
#[tauri::command]
pub async fn cancel_install(launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.cancel_install()
}
#[tauri::command]
pub async fn launch(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.launch(&app).await
}
#[tauri::command]
pub async fn stop(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.stop(&app).await
}
#[tauri::command]
pub async fn get_stack(launcher: State<'_, Launcher>) -> CmdResult<Vec<StackEvent>> {
Ok(launcher.stack_states())
}
#[tauri::command]
pub async fn sync_tiles(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.sync_tiles(&app).await
}
#[tauri::command]
pub async fn sync_now(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<()> {
launcher.sync_now(&app).await
}
#[tauri::command]
pub async fn get_sync_conflicts(app: AppHandle, launcher: State<'_, Launcher>) -> CmdResult<Vec<Conflict>> {
launcher.sync_conflicts(&app).await
}
/// `choice`: "theirs", "mine", "value" (with `value`) or "dismiss" (refused edits only).
#[tauri::command]
pub async fn resolve_sync_conflict(
app: AppHandle,
launcher: State<'_, Launcher>,
tbl: String,
key: serde_json::Value,
choice: String,
value: Option<serde_json::Value>,
) -> CmdResult<Vec<Conflict>> {
launcher.resolve_sync_conflict(&app, tbl, key, choice, value).await
}
#[tauri::command]
pub async fn get_settings(launcher: State<'_, Launcher>) -> CmdResult<Settings> {
Ok(launcher.settings().await)
}
#[tauri::command]
pub async fn set_settings(app: AppHandle, launcher: State<'_, Launcher>, settings: Settings) -> CmdResult<Status> {
launcher.set_settings(&app, settings).await
}
#[tauri::command]
pub async fn pick_folder(app: AppHandle) -> CmdResult<Option<String>> {
// The native dialog blocks its thread, so keep it off the async runtime's workers.
tauri::async_runtime::spawn_blocking(move || {
app
.dialog()
.file()
.blocking_pick_folder()
.and_then(|picked| picked.into_path().ok())
.map(|path| path.display().to_string())
})
.await
.map_err(|e| e.to_string())
}