From 48dc9ec4df9418cade2353e3eb918ffeed35cffc Mon Sep 17 00:00:00 2001 From: akanyan Date: Thu, 6 Mar 2025 23:29:13 +0000 Subject: [PATCH] feat: CLI --- package.json | 1 + rust/Cargo.lock | 45 +++++++++++++++++++++- rust/Cargo.toml | 1 + rust/capabilities/desktop.json | 14 +++++++ rust/src/appdata.rs | 2 + rust/src/lib.rs | 70 ++++++++++++++++++++++++++++++++-- rust/src/start.rs | 10 ++++- rust/tauri.conf.json | 33 +++++++++++----- 8 files changed, 161 insertions(+), 15 deletions(-) create mode 100644 rust/capabilities/desktop.json diff --git a/package.json b/package.json index 2498877..f2848a6 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@primevue/themes": "^4.3.1", "@tailwindcss/vite": "^4.0.9", "@tauri-apps/api": "^2.3.0", + "@tauri-apps/plugin-cli": "^2.2.0", "@tauri-apps/plugin-deep-link": "~2.2.0", "@tauri-apps/plugin-dialog": "~2.2.0", "@tauri-apps/plugin-fs": "^2.2.0", diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 61739f6..c326aa6 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -727,6 +727,33 @@ dependencies = [ "vec_map", ] +[[package]] +name = "clap" +version = "4.5.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "027bb0d98429ae334a8698531da7077bdf906419543a35a55c2cb1b66437d767" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5589e0cba072e0f3d23791efac0fd8627b49c829c196a492e88168e6a669d863" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + [[package]] name = "closure" version = "0.3.0" @@ -4530,6 +4557,7 @@ dependencies = [ "simple_logger", "tauri", "tauri-build", + "tauri-plugin-cli", "tauri-plugin-deep-link", "tauri-plugin-dialog", "tauri-plugin-fs", @@ -4590,7 +4618,7 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c6b5c64445ba8094a6ab0c3cd2ad323e07171012d9c98b0b15651daf1787a10" dependencies = [ - "clap", + "clap 2.34.0", "lazy_static", "structopt-derive", ] @@ -4888,6 +4916,21 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-cli" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5458ae16eac81bdbe8d9da2a9f3e01e8cdedbc381cc1727c01127542c8a61c5" +dependencies = [ + "clap 4.5.31", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.12", +] + [[package]] name = "tauri-plugin-deep-link" version = "2.2.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 15ec600..6f88286 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -42,6 +42,7 @@ junction = "1.2.0" tauri-plugin-fs = "2" [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] +tauri-plugin-cli = "2" tauri-plugin-single-instance = { version = "2", features = ["deep-link"] } [target.'cfg(target_os = "windows")'.dependencies] diff --git a/rust/capabilities/desktop.json b/rust/capabilities/desktop.json new file mode 100644 index 0000000..a33e9fd --- /dev/null +++ b/rust/capabilities/desktop.json @@ -0,0 +1,14 @@ +{ + "identifier": "desktop-capability", + "platforms": [ + "macOS", + "windows", + "linux" + ], + "windows": [ + "main" + ], + "permissions": [ + "cli:default" + ] +} \ No newline at end of file diff --git a/rust/src/appdata.rs b/rust/src/appdata.rs index a2a91b6..d6f34d5 100644 --- a/rust/src/appdata.rs +++ b/rust/src/appdata.rs @@ -15,6 +15,7 @@ pub struct AppData { pub profile: Option, pub pkgs: PackageStore, pub cfg: GlobalConfig, + pub remain_open: bool, } impl AppData { @@ -32,6 +33,7 @@ impl AppData { profile, pkgs: PackageStore::new(apph.clone()), cfg, + remain_open: true } } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index fc8319b..1297f31 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -10,18 +10,20 @@ mod download_handler; mod appdata; mod display; +use anyhow::anyhow; use closure::closure; use appdata::AppData; +use model::misc::Game; use pkg::PkgKey; use profile::Profile; use tauri::{Listener, Manager}; use tauri_plugin_deep_link::DeepLinkExt; +use tauri_plugin_cli::CliExt; use tokio::{sync::Mutex, fs, try_join}; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub async fn run(_args: Vec) { - simple_logger::init_with_env() - .expect("Unable to initialize the logger"); + simple_logger::init_with_env().expect("Unable to initialize the logger"); log::info!( "Running from {}", @@ -62,6 +64,7 @@ pub async fn run(_args: Vec) { } } })) + .plugin(tauri_plugin_cli::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_deep_link::init()) .plugin(tauri_plugin_dialog::init()) @@ -72,7 +75,38 @@ pub async fn run(_args: Vec) { util::init_dirs(&apph); - let app_data = AppData::new(app.handle().clone()); + let mut app_data = AppData::new(app.handle().clone()); + let start_immediately; + + if let Ok(matches) = app.cli().matches() { + let start_arg = matches.args.get("start").expect("Invalid argument configuration"); + let game_arg = matches.args.get("game").expect("Invalid argument configuration"); + let name_arg = matches.args.get("name").expect("Invalid argument configuration"); + log::debug!("{:?} {:?} {:?}", start_arg, game_arg, name_arg); + if start_arg.occurrences > 0 { + start_immediately = true; + app_data.remain_open = false; + } else { + tauri::WebviewWindowBuilder::new(app, "main", tauri::WebviewUrl::App("index.html".into())) + .title("STARTLINER") + .inner_size(600f64, 500f64) + .min_inner_size(600f64, 500f64) + .build()?; + start_immediately = false; + } + + if game_arg.occurrences == 1 && name_arg.occurrences == 1 { + let game = game_arg.value.as_str().unwrap(); + let name = name_arg.value.as_str().unwrap(); + + app_data.switch_profile( + Game::from_str(game).ok_or_else(|| anyhow!("Invalid game"))?, + name.to_owned() + )?; + } + } else { + return Err(anyhow!("Invalid command line arguments").into()); + } app.manage(Mutex::new(app_data)); app.deep_link().register_all()?; @@ -103,6 +137,36 @@ pub async fn run(_args: Vec) { }); })); + app.listen("launch-end", closure!(clone apph, |_| { + let apph = apph.clone(); + tauri::async_runtime::spawn(async move { + let mutex = apph.state::>(); + let appd = mutex.lock().await; + if !appd.remain_open { + apph.exit(0); + } + }); + })); + + if start_immediately == true { + let apph_clone = apph.clone(); + tauri::async_runtime::spawn(async { + let apph_clone_clone = apph_clone.clone(); + { + let mtx = apph_clone.state::>(); + let mut appd = mtx.lock().await; + if let Err(e) = appd.pkgs.reload_all().await { + log::error!("Unable to reload packages: {}", e); + apph_clone.exit(1); + } + } + if let Err(e) = cmd::startline(apph_clone).await { + log::error!("Unable to launch: {}", e); + apph_clone_clone.exit(1); + } + }); + } + Ok(()) }) .invoke_handler(tauri::generate_handler![ diff --git a/rust/src/start.rs b/rust/src/start.rs index d5fa082..7eb855f 100644 --- a/rust/src/start.rs +++ b/rust/src/start.rs @@ -110,6 +110,8 @@ pub async fn start(p: &Profile, app: AppHandle) -> Result<()> { amd_builder.env("OPENSSL_ia32cap", ":~0x20000000"); } + pkill("amdaemon.exe").await; + log::info!("Launching amdaemon: {:?}", amd_builder); log::info!("Launching mu3: {:?}", game_builder); @@ -127,7 +129,9 @@ pub async fn start(p: &Profile, app: AppHandle) -> Result<()> { (game.wait().await.expect("mu3 failed to run"), "mu3.exe") }); - _ = app.emit("launch-start", ""); + if let Err(e) = app.emit("launch-start", "") { + log::warn!("Unable to emit launch-start: {}", e); + } let (rc, process_name) = set.join_next().await.expect("No spawn").expect("No result"); @@ -143,7 +147,9 @@ pub async fn start(p: &Profile, app: AppHandle) -> Result<()> { log::debug!("Fin"); - _ = app.emit("launch-end", ""); + if let Err(e) = app.emit("launch-start", "") { + log::warn!("Unable to emit launch-end: {}", e); + } #[cfg(target_os = "windows")] { diff --git a/rust/tauri.conf.json b/rust/tauri.conf.json index 3692fa9..98ff5fd 100644 --- a/rust/tauri.conf.json +++ b/rust/tauri.conf.json @@ -17,18 +17,33 @@ "desktop": { "schemes": ["rainycolor"] } + }, + "cli": { + "description": "STARTLINER CLI", + "args": [ + { + "short": "s", + "name": "start", + "description": "Just start" + }, + { + "short": "g", + "name": "game", + "description": "Game", + "takesValue": true, + "possibleValues": ["chunithm", "ongeki"] + }, + { + "short": "n", + "name": "name", + "takesValue": true, + "description": "Profile name" + } + ] } }, "app": { - "windows": [ - { - "title": "STARTLINER", - "width": 600, - "height": 500, - "minWidth": 600, - "minHeight": 500 - } - ], + "windows": [], "security": { "csp": { "img-src": "'self' asset: https: http://asset.localhost blob: data:"