diff --git a/.vscode/settings.json b/.vscode/settings.json index 8beb3b56..b7be1aee 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,10 +24,11 @@ "**/routeTree.gen.ts": true }, "rust-analyzer.procMacro.enable": true, - "rust-analyzer.procMacro.attributes.enable": false, + "rust-analyzer.procMacro.attributes.enable": true, + "rust-analyzer.diagnostics.disabled": [ + "macro-error" // TODO: Remove this once taurpc upgrades to latest specta + ], "rust-analyzer.procMacro.ignored": { - "async-trait": ["async_trait"], - "specta": ["specta"], "tauri-specta": ["command"] }, "rust-analyzer.check.command": "check", diff --git a/Cargo.lock b/Cargo.lock index 2108adba..2d05a4b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2518,6 +2518,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -3365,7 +3374,6 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-updater", "tauri-plugin-window-state", - "tauri-specta", "thiserror 2.0.12", "tokio", "tracing", @@ -3415,7 +3423,7 @@ dependencies = [ "specta", "sysinfo", "tauri", - "tauri-specta", + "taurpc", "tempfile", "thiserror 2.0.12", "tokio", @@ -3475,7 +3483,7 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-updater", "tauri-plugin-window-state", - "tauri-specta", + "taurpc", "thiserror 2.0.12", "tokio", "tracing", @@ -5231,13 +5239,14 @@ dependencies = [ [[package]] name = "specta" version = "2.0.0-rc.22" -source = "git+https://github.com/specta-rs/specta?rev=30661263250e7b6f9b36cdc56e6c843ac5abb226#30661263250e7b6f9b36cdc56e6c843ac5abb226" +source = "git+https://github.com/specta-rs/specta?rev=5758221bc9a11d6e827851273fd665662e90ceef#5758221bc9a11d6e827851273fd665662e90ceef" dependencies = [ "chrono", + "ctor", "paste", "serde", "serde_json", - "specta-macros", + "specta-macros 2.0.0-rc.18 (git+https://github.com/specta-rs/specta?rev=5758221bc9a11d6e827851273fd665662e90ceef)", "thiserror 1.0.64", "tokio", "uuid 1.16.0", @@ -5246,7 +5255,19 @@ dependencies = [ [[package]] name = "specta-macros" version = "2.0.0-rc.18" -source = "git+https://github.com/specta-rs/specta?rev=30661263250e7b6f9b36cdc56e6c843ac5abb226#30661263250e7b6f9b36cdc56e6c843ac5abb226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0074b9e30ed84c6924eb63ad8d2fe71cdc82628525d84b1fcb1f2fd40676517" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "specta-macros" +version = "2.0.0-rc.18" +source = "git+https://github.com/specta-rs/specta?rev=5758221bc9a11d6e827851273fd665662e90ceef#5758221bc9a11d6e827851273fd665662e90ceef" dependencies = [ "Inflector", "proc-macro2", @@ -5257,7 +5278,17 @@ dependencies = [ [[package]] name = "specta-serde" version = "0.0.9" -source = "git+https://github.com/specta-rs/specta?rev=30661263250e7b6f9b36cdc56e6c843ac5abb226#30661263250e7b6f9b36cdc56e6c843ac5abb226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77216504061374659e7245eac53d30c7b3e5fe64b88da97c753e7184b0781e63" +dependencies = [ + "specta", + "thiserror 1.0.64", +] + +[[package]] +name = "specta-serde" +version = "0.0.9" +source = "git+https://github.com/specta-rs/specta?rev=5758221bc9a11d6e827851273fd665662e90ceef#5758221bc9a11d6e827851273fd665662e90ceef" dependencies = [ "specta", "thiserror 1.0.64", @@ -5266,10 +5297,10 @@ dependencies = [ [[package]] name = "specta-typescript" version = "0.0.9" -source = "git+https://github.com/specta-rs/specta?rev=30661263250e7b6f9b36cdc56e6c843ac5abb226#30661263250e7b6f9b36cdc56e6c843ac5abb226" +source = "git+https://github.com/specta-rs/specta?rev=5758221bc9a11d6e827851273fd665662e90ceef#5758221bc9a11d6e827851273fd665662e90ceef" dependencies = [ "specta", - "specta-serde", + "specta-serde 0.0.9 (git+https://github.com/specta-rs/specta?rev=5758221bc9a11d6e827851273fd665662e90ceef)", "thiserror 1.0.64", ] @@ -6089,34 +6120,6 @@ dependencies = [ "wry", ] -[[package]] -name = "tauri-specta" -version = "2.0.0-rc.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23c0132dd3cf6064e5cd919b82b3f47780e9280e7b5910babfe139829b76655" -dependencies = [ - "heck 0.5.0", - "serde", - "serde_json", - "specta", - "specta-typescript", - "tauri", - "tauri-specta-macros", - "thiserror 2.0.12", -] - -[[package]] -name = "tauri-specta-macros" -version = "2.0.0-rc.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a4aa93823e07859546aa796b8a5d608190cd8037a3a5dce3eb63d491c34bda8" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "syn 2.0.101", -] - [[package]] name = "tauri-utils" version = "2.4.0" @@ -6166,6 +6169,39 @@ dependencies = [ "toml", ] +[[package]] +name = "taurpc" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f21c40a46c1e5f859eeb51427706dde816b55d6d9baf2dab4d952aff191a1" +dependencies = [ + "anyhow", + "heck 0.5.0", + "itertools", + "serde", + "serde_json", + "specta", + "specta-macros 2.0.0-rc.18 (registry+https://github.com/rust-lang/crates.io-index)", + "specta-serde 0.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "specta-typescript", + "tauri", + "taurpc-macros", + "tokio", +] + +[[package]] +name = "taurpc-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39137974c051ae93c437b004082541bd0f2f0f69c0d9e30c16266983cd952d4f" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn 2.0.101", +] + [[package]] name = "tempfile" version = "3.19.1" diff --git a/Cargo.toml b/Cargo.toml index 84fb4494..c2860f37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,13 +61,10 @@ specta = { version = "=2.0.0-rc.22", features = [ "serde_json", "function", "derive", - "tokio" + "tokio", ] } specta-typescript = { version = "=0.0.9" } -tauri-specta = { version = "=2.0.0-rc.21", features = [ - "derive", - "typescript" -] } +taurpc = { version = "=0.5.0" } # logging tracing = { version = "0.1" } @@ -182,8 +179,11 @@ proc-macro2 = { version = "1" } [patch.crates-io] # tauri-specta = { git = "https://github.com/specta-rs/tauri-specta", rev = "6fe9532a44b8b683b4dacd178b0120ee585bea23" } -specta = { git = "https://github.com/specta-rs/specta", rev = "30661263250e7b6f9b36cdc56e6c843ac5abb226" } -specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "30661263250e7b6f9b36cdc56e6c843ac5abb226" } +specta = { git = "https://github.com/specta-rs/specta", rev = "5758221bc9a11d6e827851273fd665662e90ceef" } +specta-typescript = { git = "https://github.com/specta-rs/specta", rev = "5758221bc9a11d6e827851273fd665662e90ceef" } + +[workspace.lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = [ 'cfg(feature, values("used_linker"))' ] } [workspace.lints.clippy] all = { level = "warn", priority = -1 } @@ -253,4 +253,4 @@ codegen-units = 1 # compile crate one after another for better optimization lto = true # enable link time optimization opt-level = "s" # optimize binary panic = "abort" # strip panic cleanup logic -strip = true # remove debug symbols +strip = true diff --git a/apps/oneclient/desktop/Cargo.toml b/apps/oneclient/desktop/Cargo.toml index dc1368bf..b54a2104 100644 --- a/apps/oneclient/desktop/Cargo.toml +++ b/apps/oneclient/desktop/Cargo.toml @@ -39,7 +39,6 @@ tauri-plugin-dialog = { workspace = true } tauri-plugin-deep-link = { workspace = true } # code gen -tauri-specta = { workspace = true } specta = { workspace = true } specta-typescript = { workspace = true } strum = { workspace = true } diff --git a/apps/oneclient/desktop/src/api/commands.rs b/apps/oneclient/desktop/src/api/commands.rs index 48fa8e68..85ed4cc7 100644 --- a/apps/oneclient/desktop/src/api/commands.rs +++ b/apps/oneclient/desktop/src/api/commands.rs @@ -1,30 +1,30 @@ -pub mod cluster; -pub mod users; -pub mod processor; -pub mod package; -pub mod other; -pub mod settings; +// pub mod cluster; +// pub mod users; +// pub mod processor; +// pub mod package; +// pub mod other; +// pub mod settings; -#[macro_export(local_inner_macros)] -macro_rules! collect_commands { - () => {{ - use $crate::api::commands::*; - tauri_specta::collect_commands![ - users::get_users, - users::get_user, - users::remove_user, - users::get_default_user, - users::set_default_user, - users::begin_ms_flow, +// #[macro_export(local_inner_macros)] +// macro_rules! collect_commands { +// () => {{ +// use $crate::api::commands::*; +// tauri_specta::collect_commands![ +// users::get_users, +// users::get_user, +// users::remove_user, +// users::get_default_user, +// users::set_default_user, +// users::begin_ms_flow, - cluster::create_cluster, - cluster::get_clusters, - cluster::get_cluster_by_id, +// cluster::create_cluster, +// cluster::get_clusters, +// cluster::get_cluster_by_id, - settings::get_global_profile, - settings::get_profile_or_default, +// settings::get_global_profile, +// settings::get_profile_or_default, - other::open_dev_tools, - ] - }}; -} +// other::open_dev_tools, +// ] +// }}; +// } diff --git a/apps/oneclient/desktop/src/api/events.rs b/apps/oneclient/desktop/src/api/events.rs index 42197de5..c347136f 100644 --- a/apps/oneclient/desktop/src/api/events.rs +++ b/apps/oneclient/desktop/src/api/events.rs @@ -1,8 +1,8 @@ -#[macro_export] -macro_rules! collect_events { - () => {{ - tauri_specta::collect_events! { - onelauncher_core::api::proxy::event::LauncherEvent - } - }}; -} \ No newline at end of file +// #[macro_export] +// macro_rules! collect_events { +// () => {{ +// tauri_specta::collect_events! { +// onelauncher_core::api::proxy::event::LauncherEvent +// } +// }}; +// } \ No newline at end of file diff --git a/apps/oneclient/desktop/src/lib.rs b/apps/oneclient/desktop/src/lib.rs index 603bccd2..41ed8fbf 100644 --- a/apps/oneclient/desktop/src/lib.rs +++ b/apps/oneclient/desktop/src/lib.rs @@ -7,7 +7,7 @@ use tauri::{Emitter, Manager}; pub mod api; pub mod constants; -pub mod ext; +// pub mod ext; #[derive(Clone, serde::Serialize)] pub struct SingleInstancePayload { @@ -38,19 +38,19 @@ async fn initialize_core() -> LauncherResult<()> { #[tracing::instrument(skip_all)] async fn initialize_tauri(builder: tauri::Builder) -> LauncherResult { - let prebuild = tauri_specta::Builder::::new() - .commands(collect_commands!()) - .events(collect_events!()); + // let prebuild = tauri_specta::Builder::::new() + // .commands(collect_commands!()) + // .events(collect_events!()); - #[cfg(debug_assertions)] - prebuild - .export( - specta_typescript::Typescript::default() - .bigint(specta_typescript::BigIntExportBehavior::BigInt) - .formatter(ext::specta::formatter), - "../frontend/src/bindings.gen.ts", - ) - .expect("failed to export debug bindings!"); + // #[cfg(debug_assertions)] + // prebuild + // .export( + // specta_typescript::Typescript::default() + // .bigint(specta_typescript::BigIntExportBehavior::BigInt) + // .formatter(ext::specta::formatter), + // "../frontend/src/bindings.gen.ts", + // ) + // .expect("failed to export debug bindings!"); let builder = builder .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { @@ -60,15 +60,15 @@ async fn initialize_tauri(builder: tauri::Builder) -> LauncherResult })) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_clipboard_manager::init()) - .plugin(ext::updater::plugin()) - .manage(ext::updater::State::default()) + // .plugin(ext::updater::plugin()) + // .manage(ext::updater::State::default()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_deep_link::init()) // .plugin(api::init()) .menu(tauri::menu::Menu::new) - .invoke_handler(prebuild.invoke_handler()) + // .invoke_handler(prebuild.invoke_handler()) .setup(move |app| { - prebuild.mount_events(app.handle()); + // prebuild.mount_events(app.handle()); setup_window(app.handle()).expect("failed to setup main window"); Ok(()) }); diff --git a/apps/onelauncher/desktop/Cargo.toml b/apps/onelauncher/desktop/Cargo.toml index 42d59941..1d613caa 100644 --- a/apps/onelauncher/desktop/Cargo.toml +++ b/apps/onelauncher/desktop/Cargo.toml @@ -39,7 +39,7 @@ tauri-plugin-dialog = { workspace = true } tauri-plugin-deep-link = { workspace = true } # code gen -tauri-specta = { workspace = true } +taurpc = { workspace = true } specta = { workspace = true } specta-typescript = { workspace = true } strum = { workspace = true } diff --git a/apps/onelauncher/desktop/src/api/commands.rs b/apps/onelauncher/desktop/src/api/commands.rs index 48fa8e68..7735c9af 100644 --- a/apps/onelauncher/desktop/src/api/commands.rs +++ b/apps/onelauncher/desktop/src/api/commands.rs @@ -1,30 +1,27 @@ -pub mod cluster; -pub mod users; -pub mod processor; -pub mod package; -pub mod other; -pub mod settings; +use onelauncher_core::error::{DaoError, LauncherResult}; +use tauri::Runtime; -#[macro_export(local_inner_macros)] -macro_rules! collect_commands { - () => {{ - use $crate::api::commands::*; - tauri_specta::collect_commands![ - users::get_users, - users::get_user, - users::remove_user, - users::get_default_user, - users::set_default_user, - users::begin_ms_flow, +#[taurpc::procedures(path = "onelauncher", export_to = "../frontend/src/bindings.gen.ts")] +pub trait OneLauncherApi { + async fn return_error() -> LauncherResult<()>; + + async fn open_dev_tools(webview_window: tauri::WebviewWindow); +} + +#[taurpc::ipc_type] +pub struct OneLauncherApiImpl; + +#[taurpc::resolvers] +impl OneLauncherApi for OneLauncherApiImpl { + + async fn return_error(self) -> LauncherResult<()> { + let err = DaoError::NotFound.into(); + Err(err) + } + + async fn open_dev_tools(self, webview_window: tauri::WebviewWindow) { + #[cfg(feature = "devtools")] + webview_window.open_devtools(); + } - cluster::create_cluster, - cluster::get_clusters, - cluster::get_cluster_by_id, - - settings::get_global_profile, - settings::get_profile_or_default, - - other::open_dev_tools, - ] - }}; } diff --git a/apps/onelauncher/desktop/src/api/commands/cluster.rs b/apps/onelauncher/desktop/src/api/commands/cluster.rs deleted file mode 100644 index f339a12a..00000000 --- a/apps/onelauncher/desktop/src/api/commands/cluster.rs +++ /dev/null @@ -1,262 +0,0 @@ -// use std::collections::HashMap; -// use std::path::PathBuf; -// use std::str::FromStr; - -// use onelauncher_core::cluster::content::logger; -// use onelauncher_core::cluster::{self}; -// use onelauncher_core::data::{Loader, PackageData}; -// use onelauncher_core::processor::DetailedProcess; -// use onelauncher_core::store::{Cluster, ClusterPath}; -// use onelauncher_core::api::java::JavaInfo; -// use onelauncher_core::store::State; -// use serde::{Deserialize, Serialize}; -// use specta::Type; -// use uuid::Uuid; - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster(uuid: Uuid) -> Result { -// (cluster::get_by_uuid(uuid).await?).map_or_else(|| Err("Cluster does not exist".into()), Ok) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_clusters() -> Result, String> { -// Ok(cluster::list().await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_clusters_grouped() -> Result>, String> { -// Ok(cluster::list_grouped().await?) -// } - -// #[derive(Serialize, Deserialize, Type)] -// pub struct CreateCluster { -// name: String, -// mc_version: String, -// mod_loader: Loader, -// loader_version: Option, -// icon: Option, -// icon_url: Option, -// package_data: Option, -// skip: Option, -// skip_watch: Option, -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn create_cluster(props: CreateCluster) -> Result { -// let path = cluster::create::create_cluster( -// props.name, -// props.mc_version, -// props.mod_loader, -// props.loader_version, -// props.icon, -// props.icon_url, -// props.package_data, -// props.skip, -// props.skip_watch, -// ) -// .await?; - -// if let Some(cluster) = cluster::get(&path).await? { -// Ok(cluster.uuid) -// } else { -// Err("Cluster does not exist".to_string()) -// } -// } - -// /// Updates the cluster with the given UUID. The cluster only updates game setting fields -// #[specta::specta] -// #[tauri::command] -// pub async fn edit_game_settings(uuid: Uuid, new_cluster: Cluster) -> Result<(), String> { -// let cluster_path = ClusterPath::find_by_uuid(uuid).await?; - -// cluster::edit(&cluster_path, |old| { -// old.force_fullscreen = new_cluster.force_fullscreen; -// old.resolution = new_cluster.resolution; -// old.memory = new_cluster.memory; -// old.init_hooks.clone_from(&new_cluster.init_hooks); -// old.java.clone_from(&new_cluster.java); - -// async move { Ok(()) } -// }) -// .await?; - -// State::sync().await?; - -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn edit_cluster_meta( -// uuid: Uuid, -// name: Option, -// icon_path: Option, -// ) -> Result<(), String> { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster does not exist")?; - -// cluster::edit(&cluster.cluster_path(), move |cluster| { -// if let Some(name) = name.clone() { -// cluster.meta.name = name; -// } - -// async move { Ok(()) } -// }) -// .await?; - -// let icon_path = icon_path.and_then(|x| PathBuf::from_str(x.as_str()).ok()); -// cluster::edit_icon(&cluster.cluster_path(), icon_path.as_deref()).await?; - -// State::sync().await?; - -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn remove_cluster(uuid: Uuid) -> Result<(), String> { -// let path = ClusterPath::find_by_uuid(uuid).await?; -// Ok(cluster::remove(&path).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn run_cluster(uuid: Uuid) -> Result { -// let path = ClusterPath::find_by_uuid(uuid).await?; -// let c_lock = cluster::run_default(&path).await?; -// let child = &*c_lock.read().await; - -// Ok(DetailedProcess::from_processor_child(child).await) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster_logs(uuid: Uuid) -> Result, String> { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster not found")?; -// let logs = logger::get_logs(&cluster.cluster_path(), None) -// .await? -// .iter() -// .map(|x| x.log_file.clone()) -// .collect(); -// Ok(logs) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster_log(uuid: Uuid, log_name: String) -> Result { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster not found")?; -// let log = logger::get_output_by_file(&cluster.cluster_path(), logger::LogType::Info, &log_name) -// .await?; -// Ok(log.0) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn upload_log(uuid: Uuid, log_name: String) -> Result { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster not found")?; -// let log = logger::get_output_by_file(&cluster.cluster_path(), logger::LogType::Info, &log_name) -// .await?; - -// let id = logger::upload_log(&cluster.cluster_path(), log).await?; -// Ok(id) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_screenshots(uuid: Uuid) -> Result, String> { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster not found")?; - -// let screenshots = -// cluster::content::screenshots::get_screenshots(&cluster.cluster_path()).await?; - -// Ok(screenshots) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_worlds(uuid: Uuid) -> Result, String> { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster not found")?; - -// let screenshots = cluster::content::worlds::get_worlds(&cluster.cluster_path()).await?; - -// Ok(screenshots) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_optimal_java_version(uuid: Uuid) -> Result { -// let cluster_path = cluster::get_by_uuid(uuid).await?.ok_or("Cluster not found")?.cluster_path(); -// Ok(cluster::get_optimal_java_version(&cluster_path).await?.ok_or("No Java version found")?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn repair_cluster(uuid: Uuid) -> Result<(), String> { -// let cluster = cluster::get_by_uuid(uuid) -// .await? -// .ok_or("cluster not found")?; - -// cluster::repair_cluster(&cluster.cluster_path()).await?; - -// Ok(()) -// } - -use std::ops::Deref; - -use onelauncher_core::{api::{cluster, setting_profiles}, entity::{clusters::Model, icon::Icon, loader::GameLoader}, error::LauncherResult, migration::sea_orm}; -use serde::{Deserialize, Serialize}; -use specta::Type; -use crate::api::error::SerializableResult; - -#[derive(Serialize, Deserialize, Type)] -pub struct CreateCluster { - name: String, - mc_version: String, - mc_loader: GameLoader, - mc_loader_version: Option, - icon_url: Option -} - -#[specta::specta] -#[tauri::command] -pub async fn create_cluster(options: CreateCluster) -> SerializableResult { - let thing = cluster::create_cluster(&options.name, &options.mc_version, options.mc_loader, options.mc_loader_version.as_deref(), options.icon_url).await?; - - // cluster::prepare_cluster(&mut thing.clone(), Some(false)); - - if setting_profiles::dao::get_profile_by_name(&options.name).await?.is_none() { - setting_profiles::create_profile(&options.name, async |mut profile| { - profile.mem_max = sea_orm::ActiveValue::Set(Some(2048)); - Ok(profile) - }).await?; - } - - Ok(thing) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_clusters() -> SerializableResult> { - Ok(cluster::dao::get_all_clusters().await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_cluster_by_id(id: i64) -> SerializableResult> { - Ok(cluster::dao::get_cluster_by_id(id).await?) -} \ No newline at end of file diff --git a/apps/onelauncher/desktop/src/api/commands/other.rs b/apps/onelauncher/desktop/src/api/commands/other.rs deleted file mode 100644 index 8d3014b3..00000000 --- a/apps/onelauncher/desktop/src/api/commands/other.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[onelauncher_core::command] -#[allow(unused_parens)] -pub fn open_dev_tools(webview: tauri::WebviewWindow) { - #[cfg(feature = "devtools")] - webview.open_devtools(); -} \ No newline at end of file diff --git a/apps/onelauncher/desktop/src/api/commands/package.rs b/apps/onelauncher/desktop/src/api/commands/package.rs deleted file mode 100644 index 165a4b23..00000000 --- a/apps/onelauncher/desktop/src/api/commands/package.rs +++ /dev/null @@ -1,234 +0,0 @@ -// use std::path::PathBuf; - -// use onelauncher::cluster::content::package; -// use onelauncher::data::{Loader, ManagedPackage, ManagedUser, ManagedVersion, PackageType}; -// use onelauncher::package::content::Providers; -// use onelauncher::package::import::ImportType; -// use onelauncher::store::{ -// Author, ClusterPath, Package, PackageBody, PackagePath, ProviderSearchResults, -// }; -// use onelauncher::utils::pagination::Pagination; -// use uuid::Uuid; - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_launcher_instances( -// launcher: ImportType, -// path: Option, -// ) -> Result<(String, Vec), String> { -// let (dir, instances) = onelauncher::api::package::import::get_launcher_instances(launcher, path).await?; -// Ok(( -// dir.to_str().ok_or("could not convert pathbuf to string")?.to_string(), -// instances -// )) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn import_instances( -// launcher: ImportType, -// base_path: PathBuf, -// instances: Vec, -// ) -> Result<(), String> { -// onelauncher::api::package::import::import_instances(launcher, base_path, instances).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_package( -// provider: Providers, -// project_id: String, -// ) -> Result { -// Ok(provider.get(&project_id).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_packages( -// provider: Providers, -// project_ids: Vec, -// ) -> Result, String> { -// Ok(provider.get_multiple(&project_ids).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_all_provider_package_versions( -// provider: Providers, -// project_id: String, -// game_versions: Option>, -// loaders: Option>, -// page: Option, -// page_size: Option, -// ) -> Result<(Vec, Pagination), String> { -// Ok(provider -// .get_all_versions(&project_id, game_versions, loaders, page, page_size) -// .await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_package_versions( -// provider: Providers, -// versions: Vec, -// ) -> Result, String> { -// Ok(provider.get_versions(versions).await?) -// } - -// #[derive(specta::Type, serde::Deserialize, serde::Serialize)] -// pub struct ProviderSearchQuery { -// query: Option, -// limit: Option, -// offset: Option, -// game_versions: Option>, -// categories: Option>, -// loaders: Option>, -// package_types: Option>, -// open_source: Option, -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn search_provider_packages( -// provider: Providers, -// query: ProviderSearchQuery, -// ) -> Result { -// Ok(provider -// .search( -// query.query, -// query.limit, -// query.offset, -// query.game_versions, -// query.categories, -// query.loaders, -// query.package_types, -// query.open_source, -// ) -// .await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_provider_authors( -// provider: Providers, -// author: Author, -// ) -> Result, String> { -// Ok(provider.get_authors(&author).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_package_body(provider: Providers, body: PackageBody) -> Result { -// Ok(provider.get_package_body(&body).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn download_provider_package( -// provider: Providers, -// package_id: String, -// cluster_id: Uuid, -// game_version: Option, -// loader: Option, -// package_version: Option, -// ) -> Result<(), String> { -// let mut cluster = onelauncher::cluster::get_by_uuid(cluster_id) -// .await? -// .ok_or("cluster not found")?; - -// let mgd_pkg = provider.get(&package_id).await?; - -// let (pkg_path, pkg) = package::download_package( -// &mgd_pkg, -// &mut cluster, -// game_version, -// loader, -// package_version, -// ) -// .await?; - -// package::add_package( -// &cluster.cluster_path(), -// pkg_path, -// pkg, -// ) -// .await?; - -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster_package( -// cluster_path: ClusterPath, -// package_path: PackagePath, -// package_type: PackageType, -// ) -> Result { -// Ok(package::get_package(&cluster_path, &package_path, package_type).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_cluster_packages( -// cluster_path: ClusterPath, -// package_type: PackageType, -// ) -> Result, String> { -// Ok(package::get_packages(&cluster_path, package_type).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn add_cluster_package( -// cluster_path: ClusterPath, -// file_name: String, -// package_type: PackageType, -// pkg: Package, -// ) -> Result<(), String> { -// let package_path = PackagePath::new(package_type, file_name)?; -// package::add_package(&cluster_path, package_path, pkg).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn remove_cluster_package( -// cluster_path: ClusterPath, -// file_name: String, -// package_type: PackageType, -// ) -> Result<(), String> { -// let package_path = PackagePath::new(package_type, file_name)?; -// package::remove_package(&cluster_path, &package_path).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn set_cluster_package_enabled( -// cluster_path: ClusterPath, -// file_name: String, -// package_type: PackageType, -// enabled: bool, -// ) -> Result<(), String> { -// let package_path = PackagePath::new(package_type, file_name)?; -// package::set_package_enabled(&cluster_path, &package_path, enabled).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn sync_cluster_packages(cluster_path: ClusterPath) -> Result<(), String> { -// package::sync_packages(&cluster_path).await?; -// Ok(()) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn sync_cluster_packages_by_type( -// cluster_path: ClusterPath, -// package_type: PackageType, -// clear: Option, -// ) -> Result<(), String> { -// package::sync_packages_by_type(&cluster_path, package_type, clear).await?; -// Ok(()) -// } diff --git a/apps/onelauncher/desktop/src/api/commands/processor.rs b/apps/onelauncher/desktop/src/api/commands/processor.rs deleted file mode 100644 index 84a18027..00000000 --- a/apps/onelauncher/desktop/src/api/commands/processor.rs +++ /dev/null @@ -1,61 +0,0 @@ -// use chrono::{DateTime, Utc}; -// use onelauncher::processor::{self, DetailedProcess}; -// use onelauncher::store::{Cluster, ClusterPath}; -// use uuid::Uuid; - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_running_clusters() -> Result, String> { -// Ok(processor::get_running_clusters().await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn is_cluster_running(uuid: Uuid) -> Result { -// Ok(processor::is_cluster_running(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_processes_by_path(path: ClusterPath) -> Result, String> { -// Ok(processor::get_uuids_by_cluster_path(path).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_processes_detailed_by_path( -// path: ClusterPath, -// ) -> Result, String> { -// Ok(processor::get_processes_detailed_by_path(path).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_process_detailed_by_id(uuid: Uuid) -> Result { -// Ok(processor::get_process_detailed_by_id(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_user_by_process(uuid: Uuid) -> Result, String> { -// Ok(processor::get_user_by_process(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_process_started_at(uuid: Uuid) -> Result, String> { -// Ok(processor::get_process_started_at(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn get_pid_by_uuid(uuid: Uuid) -> Result { -// Ok(processor::get_pid_by_uuid(uuid).await?) -// } - -// #[specta::specta] -// #[tauri::command] -// pub async fn kill_process(uuid: Uuid) -> Result<(), String> { -// processor::kill_by_uuid(uuid).await?; -// Ok(()) -// } diff --git a/apps/onelauncher/desktop/src/api/commands/settings.rs b/apps/onelauncher/desktop/src/api/commands/settings.rs deleted file mode 100644 index 3173c553..00000000 --- a/apps/onelauncher/desktop/src/api/commands/settings.rs +++ /dev/null @@ -1,18 +0,0 @@ -use onelauncher_core::entity::setting_profiles::Model as SettingsModel; -use onelauncher_core::api::setting_profiles; - -use crate::api::error::SerializableResult; - -#[specta::specta] -#[tauri::command] -pub async fn get_global_profile() -> SerializableResult { - Ok(setting_profiles::get_global_profile().await) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_profile_or_default( - name: Option, -) -> SerializableResult { - Ok(setting_profiles::dao::get_profile_or_default(name.as_ref()).await?) -} \ No newline at end of file diff --git a/apps/onelauncher/desktop/src/api/commands/users.rs b/apps/onelauncher/desktop/src/api/commands/users.rs deleted file mode 100644 index 6c4d4a04..00000000 --- a/apps/onelauncher/desktop/src/api/commands/users.rs +++ /dev/null @@ -1,112 +0,0 @@ -use onelauncher_core::{api::credentials, error::LauncherResult, store::{credentials::{MinecraftCredentials, MinecraftLogin}, Core}}; -use tauri::{AppHandle, Manager}; -use uuid::Uuid; - -use crate::api::error::SerializableResult; - -#[specta::specta] -#[tauri::command] -pub async fn get_users() -> SerializableResult> { - Ok(credentials::users().await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_user(uuid: Uuid) -> SerializableResult { - Ok(credentials::get_user(uuid).await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn remove_user(uuid: Uuid) -> SerializableResult<()> { - Ok(credentials::remove_user(uuid).await?) -} - -#[specta::specta] -#[tauri::command] -pub async fn get_default_user( - fallback: Option, -) -> SerializableResult> { - let uuid = credentials::get_default_user().await?; - - if fallback.is_some_and(|fallback| fallback) && uuid.is_none() { - return Ok(credentials::users().await?.first().cloned()); - } - - match uuid { - Some(uuid) => Ok(Some(credentials::get_user(uuid).await?)), - None => Ok(None), - } -} - -#[specta::specta] -#[tauri::command] -pub async fn set_default_user(uuid: Option) -> SerializableResult<()> { - credentials::set_default_user(uuid).await?; - Ok(()) -} - -#[specta::specta] -#[tauri::command] -pub async fn begin_ms_flow(handle: tauri::AppHandle) -> SerializableResult> { - let flow = credentials::begin().await?; - let result = spawn_webview(handle, flow).await?; - - Ok(result) -} - -async fn spawn_webview( - handle: AppHandle, - flow: MinecraftLogin, -) -> LauncherResult> { - let now = chrono::Utc::now(); - - if let Some(win) = handle.get_webview_window("login") { - win.close()?; - } - - let win = tauri::WebviewWindowBuilder::new( - &handle, - "login", - tauri::WebviewUrl::External( - flow.redirect_uri - .parse() - .map_err(|_| anyhow::anyhow!("failed to parse auth redirect url"))?, - ), - ) - .title(format!("Login to {}", Core::get().launcher_name)) - .center() - .focused(true) - .build()?; - - win.request_user_attention(Some(tauri::UserAttentionType::Critical))?; - - while (chrono::Utc::now() - now) < chrono::Duration::minutes(10) { - if win.title().is_err() { - return Ok(None); - } - - if win - .url()? - .as_str() - .starts_with("https://login.live.com/oauth20_desktop.srf") - { - if let Some((_, code)) = win - .url()? - .query_pairs() - .find(|x| x.0 == "code") - { - win.close()?; - let value = credentials::finish(&code.clone(), flow).await?; - - return Ok(Some(value)); - } - } - - tokio::time::sleep(std::time::Duration::from_millis(50)).await; - } - - win.close()?; - - Ok(None) -} diff --git a/apps/onelauncher/desktop/src/api/error.rs b/apps/onelauncher/desktop/src/api/error.rs deleted file mode 100644 index 3185836a..00000000 --- a/apps/onelauncher/desktop/src/api/error.rs +++ /dev/null @@ -1,61 +0,0 @@ -use onelauncher_core::error::LauncherError; -use serde::{Serialize, Serializer, ser::SerializeStruct}; -use specta::Type; - -#[derive(thiserror::Error, Debug, Type)] -#[serde(tag = "kind", content = "data")] -pub enum SerializableError { - #[error(transparent)] - CoreError(#[serde(skip)] #[from] onelauncher_core::error::LauncherError), -} - -pub type SerializableResult = std::result::Result; - -macro_rules! impl_serialize { - ($($variant:ident),* $(,)?) => { - impl Serialize for SerializableError { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: Serializer, - { - match self { - SerializableError::CoreError(error) => { - display_tracing_error(error); - - let mut state = serializer.serialize_struct("CoreError", 2)?; - state.serialize_field("field_name", "CoreError")?; - state.serialize_field("message", &error.to_string())?; - state.end() - } - $( - SerializableError::$variant(message) => { - let mut state = serializer.serialize_struct(stringify!($variant), 2)?; - state.serialize_field("field_name", stringify!($variant))?; - state.serialize_field("message", &message.to_string())?; - state.end() - }, - )* - } - } - } - }; -} - -impl_serialize! {} - -pub fn display_tracing_error(err: &LauncherError) { - match get_span_trace(err) { - Some(span_trace) => { - tracing::error!(error = %err, span_trace = %span_trace); - } - None => { - tracing::error!(error = %err); - } - } -} - -pub fn get_span_trace<'a>( - error: &'a (dyn std::error::Error + 'static), -) -> Option<&'a tracing_error::SpanTrace> { - error.source().and_then(tracing_error::ExtractSpanTrace::span_trace) -} diff --git a/apps/onelauncher/desktop/src/api/events.rs b/apps/onelauncher/desktop/src/api/events.rs deleted file mode 100644 index 42197de5..00000000 --- a/apps/onelauncher/desktop/src/api/events.rs +++ /dev/null @@ -1,8 +0,0 @@ -#[macro_export] -macro_rules! collect_events { - () => {{ - tauri_specta::collect_events! { - onelauncher_core::api::proxy::event::LauncherEvent - } - }}; -} \ No newline at end of file diff --git a/apps/onelauncher/desktop/src/api/mod.rs b/apps/onelauncher/desktop/src/api/mod.rs index 4a937850..5bfa754f 100644 --- a/apps/onelauncher/desktop/src/api/mod.rs +++ b/apps/onelauncher/desktop/src/api/mod.rs @@ -1,4 +1,2 @@ pub mod commands; -pub mod error; -pub mod events; pub mod statics; diff --git a/apps/onelauncher/desktop/src/ext/updater.rs b/apps/onelauncher/desktop/src/ext/updater.rs index 865274b0..22ec0b57 100644 --- a/apps/onelauncher/desktop/src/ext/updater.rs +++ b/apps/onelauncher/desktop/src/ext/updater.rs @@ -1,117 +1,117 @@ -use onelauncher_core::store::Core; -use tauri::plugin::TauriPlugin; -use tauri::{Emitter, Runtime}; -use tauri_plugin_updater::{Update as TauriPluginUpdate, UpdaterExt}; -use tokio::sync::Mutex; +// use onelauncher_core::store::Core; +// use tauri::plugin::TauriPlugin; +// use tauri::{Emitter, Runtime}; +// use tauri_plugin_updater::{Update as TauriPluginUpdate, UpdaterExt}; +// use tokio::sync::Mutex; -#[derive(Debug, Clone, specta::Type, serde::Serialize)] -pub struct Update { - pub version: String, -} +// #[derive(Debug, Clone, specta::Type, serde::Serialize)] +// pub struct Update { +// pub version: String, +// } -impl Update { - fn new(update: &TauriPluginUpdate) -> Self { - Self { - version: update.version.clone(), - } - } -} +// impl Update { +// fn new(update: &TauriPluginUpdate) -> Self { +// Self { +// version: update.version.clone(), +// } +// } +// } -#[derive(Default)] -pub struct State { - install_lock: Mutex<()>, -} +// #[derive(Default)] +// pub struct State { +// install_lock: Mutex<()>, +// } -async fn get_update(app: tauri::AppHandle) -> Result, String> { - app.updater_builder() - .header("X-OneLauncher-Version", "stable") - .map_err(|e| e.to_string())? - .build() - .map_err(|e| e.to_string())? - .check() - .await - .map_err(|e| e.to_string()) -} +// async fn get_update(app: tauri::AppHandle) -> Result, String> { +// app.updater_builder() +// .header("X-OneLauncher-Version", "stable") +// .map_err(|e| e.to_string())? +// .build() +// .map_err(|e| e.to_string())? +// .check() +// .await +// .map_err(|e| e.to_string()) +// } -#[derive(Debug, Clone, serde::Serialize, specta::Type, tauri_specta::Event)] -#[serde(rename_all = "camelCase", tag = "status")] -pub enum UpdateEvent { - Loading, - Error(String), - UpdateAvailable { update: Update }, - NoUpdateAvailable, - Installing, -} +// #[derive(Debug, Clone, serde::Serialize, specta::Type, tauri_specta::Event)] +// #[serde(rename_all = "camelCase", tag = "status")] +// pub enum UpdateEvent { +// Loading, +// Error(String), +// UpdateAvailable { update: Update }, +// NoUpdateAvailable, +// Installing, +// } -#[tauri::command] -#[specta::specta] -pub async fn check_for_update(app: tauri::AppHandle) -> Result, String> { - app.emit("updater", UpdateEvent::Loading).ok(); +// #[tauri::command] +// #[specta::specta] +// pub async fn check_for_update(app: tauri::AppHandle) -> Result, String> { +// app.emit("updater", UpdateEvent::Loading).ok(); - let update = match get_update(app.clone()).await { - Ok(update) => update, - Err(e) => { - app.emit("updater", UpdateEvent::Error(e.clone())).ok(); - return Err(e); - } - }; +// let update = match get_update(app.clone()).await { +// Ok(update) => update, +// Err(e) => { +// app.emit("updater", UpdateEvent::Error(e.clone())).ok(); +// return Err(e); +// } +// }; - let update = update.map(|u| Update::new(&u)); +// let update = update.map(|u| Update::new(&u)); - app.emit( - "updater", - update.clone().map_or(UpdateEvent::NoUpdateAvailable, |u| { - UpdateEvent::UpdateAvailable { update: u } - }), - ) - .ok(); +// app.emit( +// "updater", +// update.clone().map_or(UpdateEvent::NoUpdateAvailable, |u| { +// UpdateEvent::UpdateAvailable { update: u } +// }), +// ) +// .ok(); - Ok(update) -} +// Ok(update) +// } -#[tauri::command] -#[specta::specta] -pub async fn install_update( - app: tauri::AppHandle, - state: tauri::State<'_, State>, -) -> Result<(), String> { - let Ok(lock) = state.install_lock.try_lock() else { - return Err("Update already installing".into()); - }; +// #[tauri::command] +// #[specta::specta] +// pub async fn install_update( +// app: tauri::AppHandle, +// state: tauri::State<'_, State>, +// ) -> Result<(), String> { +// let Ok(lock) = state.install_lock.try_lock() else { +// return Err("Update already installing".into()); +// }; - app.emit("updater", UpdateEvent::Installing).ok(); +// app.emit("updater", UpdateEvent::Installing).ok(); - get_update(app.clone()) - .await? - .ok_or_else(|| "No update required".to_string())? - .download_and_install(|_, _| {}, || {}) - .await - .map_err(|e| e.to_string())?; +// get_update(app.clone()) +// .await? +// .ok_or_else(|| "No update required".to_string())? +// .download_and_install(|_, _| {}, || {}) +// .await +// .map_err(|e| e.to_string())?; - drop(lock); +// drop(lock); - Ok(()) -} +// Ok(()) +// } -#[must_use] -pub fn plugin() -> TauriPlugin { - tauri::plugin::Builder::new("onelauncher-updater") - .on_page_load(|window, _| { - #[cfg(target_os = "linux")] - let updater_available = false; +// #[must_use] +// pub fn plugin() -> TauriPlugin { +// tauri::plugin::Builder::new("onelauncher-updater") +// .on_page_load(|window, _| { +// #[cfg(target_os = "linux")] +// let updater_available = false; - #[cfg(not(target_os = "linux"))] - let updater_available = true; +// #[cfg(not(target_os = "linux"))] +// let updater_available = true; - if updater_available { - window - .eval("window.__LAUNCHER_UPDATER__ = true;") - .expect("Failed to inject updater JS"); - } - }) - .js_init_script(format!( - r#"window.__ONELAUNCHER_VERSION__ = "{}";"#, - Core::get().launcher_version, - )) - .build() -} +// if updater_available { +// window +// .eval("window.__LAUNCHER_UPDATER__ = true;") +// .expect("Failed to inject updater JS"); +// } +// }) +// .js_init_script(format!( +// r#"window.__ONELAUNCHER_VERSION__ = "{}";"#, +// Core::get().launcher_version, +// )) +// .build() +// } diff --git a/apps/onelauncher/desktop/src/lib.rs b/apps/onelauncher/desktop/src/lib.rs index c7664b84..fee5c801 100644 --- a/apps/onelauncher/desktop/src/lib.rs +++ b/apps/onelauncher/desktop/src/lib.rs @@ -1,10 +1,13 @@ use onelauncher_core::api::proxy::ProxyTauri; +use onelauncher_core::api::tauri::{TauriLauncherEventApi, TauriLauncherApi}; use onelauncher_core::error::LauncherResult; use onelauncher_core::store::proxy::ProxyState; use onelauncher_core::store::semaphore::SemaphoreStore; use onelauncher_core::store::{Core, CoreOptions, Dirs, State}; use tauri::{Emitter, Manager}; +use crate::api::commands::OneLauncherApi; + pub mod api; pub mod constants; pub mod ext; @@ -38,19 +41,15 @@ async fn initialize_core() -> LauncherResult<()> { #[tracing::instrument(skip_all)] async fn initialize_tauri(builder: tauri::Builder) -> LauncherResult { - let prebuild = tauri_specta::Builder::::new() - .commands(collect_commands!()) - .events(collect_events!()); - - #[cfg(debug_assertions)] - prebuild - .export( + let router = taurpc::Router::new() + .export_config( specta_typescript::Typescript::default() .bigint(specta_typescript::BigIntExportBehavior::BigInt) .formatter(ext::specta::formatter), - "../frontend/src/bindings.gen.ts", ) - .expect("failed to export debug bindings!"); + .merge(api::commands::OneLauncherApiImpl.into_handler()) + .merge(onelauncher_core::api::tauri::TauriLauncherApiImpl.into_handler()) + .merge(onelauncher_core::api::tauri::TauriLauncherEventApiImpl.into_handler()); let builder = builder .plugin(tauri_plugin_single_instance::init(|app, argv, cwd| { @@ -60,15 +59,11 @@ async fn initialize_tauri(builder: tauri::Builder) -> LauncherResult })) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_clipboard_manager::init()) - .plugin(ext::updater::plugin()) - .manage(ext::updater::State::default()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_deep_link::init()) - // .plugin(api::init()) .menu(tauri::menu::Menu::new) - .invoke_handler(prebuild.invoke_handler()) + .invoke_handler(router.into_handler()) .setup(move |app| { - prebuild.mount_events(app.handle()); setup_window(app.handle()).expect("failed to setup main window"); Ok(()) }); @@ -93,9 +88,11 @@ async fn initialize_state(handle: &tauri::AppHandle) -> LauncherResult<()> { pub async fn run() { initialize_core().await.expect("failed to initialize core"); + let app = initialize_tauri(tauri::Builder::default()) .await .expect("failed to initialize tauri"); + initialize_state(app.handle()) .await .expect("failed to initialize state"); diff --git a/apps/onelauncher/frontend/package.json b/apps/onelauncher/frontend/package.json index 1047cfc2..52857545 100644 --- a/apps/onelauncher/frontend/package.json +++ b/apps/onelauncher/frontend/package.json @@ -36,7 +36,8 @@ "tailwind-variants": "^1.0.0", "tailwindcss": "^4.0.6", "tailwindcss-animated": "^2.0.0", - "tailwindcss-react-aria-components": "^2.0.0" + "tailwindcss-react-aria-components": "^2.0.0", + "taurpc": "^1.8.1" }, "devDependencies": { "@tanstack/eslint-config": "^0.1.0", diff --git a/apps/onelauncher/frontend/src/bindings.gen.ts b/apps/onelauncher/frontend/src/bindings.gen.ts index c408de2e..4b96eeea 100644 --- a/apps/onelauncher/frontend/src/bindings.gen.ts +++ b/apps/onelauncher/frontend/src/bindings.gen.ts @@ -1,152 +1,65 @@ -// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. +// This file has been generated by Specta. DO NOT EDIT. -/** user-defined commands */ +import type { InferCommandOutput } from 'taurpc'; +import { createTauRPCProxy as createProxy } from 'taurpc'; -/** tauri-specta globals */ +type TAURI_CHANNEL = (response: T) => void; -import type { WebviewWindow as __WebviewWindow__ } from '@tauri-apps/api/webviewWindow'; -import { - invoke as TAURI_INVOKE, -} from '@tauri-apps/api/core'; -import * as TAURI_API_EVENT from '@tauri-apps/api/event'; - -export const commands = { - async getUsers(): Promise, SerializableError>> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_users') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getUser(uuid: string): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_user', { uuid }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async removeUser(uuid: string): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('remove_user', { uuid }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getDefaultUser(fallback: boolean | null): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_default_user', { fallback }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async setDefaultUser(uuid: string | null): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('set_default_user', { uuid }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async beginMsFlow(): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('begin_ms_flow') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async createCluster(options: CreateCluster): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('create_cluster', { options }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getClusters(): Promise, SerializableError>> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_clusters') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getClusterById(id: bigint): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_cluster_by_id', { id }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getGlobalProfile(): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_global_profile') }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async getProfileOrDefault(name: string | null): Promise> { - try { - return { status: 'ok', data: await TAURI_INVOKE('get_profile_or_default', { name }) }; - } - catch (e) { - if (e instanceof Error) - throw e; - else return { status: 'error', error: e as any }; - } - }, - async openDevTools(): Promise { - await TAURI_INVOKE('open_dev_tools'); - }, -}; - -/** user-defined events */ - -export const events = __makeEvents__<{ - launcherEvent: LauncherEvent; -}>({ - launcherEvent: 'launcher-event', -}); - -/** user-defined constants */ - -/** user-defined types */ +export type ClusterError = { InvalidVersion: string } | 'MissingJavaVersion' | 'ClusterDownloading' | 'ClusterAlreadyRunning'; export type ClusterStage = 'notready' | 'downloading' | 'repairing' | 'ready'; -export interface CreateCluster { name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; icon_url: Icon | null } + +export interface CreateCluster { name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; icon: Icon | null } + +export type CryptoError = { InvalidHash: { algorithm: HashAlgorithm; expected: string; actual: string } } | 'InvalidAlgorithm'; + +export type DaoError = 'NotFound' | 'AlreadyExists' | { InvalidValue: [string, string] }; + +export type DirectoryError = 'BaseDir'; + +export type DiscordError = 'MissingClientId' | 'ConnectError'; + export type GameLoader = 'vanilla' | 'forge' | 'neoforge' | 'quilt' | 'fabric' | 'legacyfabric'; + +export type HashAlgorithm = 'Sha1' | 'Sha256' | 'Md5'; + +/** + * A wrapper around generic and unhelpful [`std::io::Error`] messages. + */ +export type IOError = { InvalidAbsolutePath: string } | { IOErrorWrapper: { path: string } } | 'IOError' | 'DeserializeError' | 'AsyncZipError'; + export type Icon = string; + +export type IncompatiblePackageType = 'McVersion' | 'Loader'; + +export type IngressError = 'NotFound'; + export interface IngressPayload { id: string; message: string; ingress_type: IngressType; percent: number | null; total: number } + export type IngressType = { Download: { file_name: string } } | 'JavaPrepare' | 'JavaCheck' | 'JavaLocate' | 'MinecraftDownload' | { PrepareCluster: { cluster_name: string } }; + +export type JavaError = { ParseVersion: [string] } | 'Execute' | 'MissingJava'; + +export type LauncherError = { type: 'DirError'; data: DirectoryError } | { type: 'IOError'; data: IOError } | { type: 'IngressError'; data: IngressError } | { type: 'JavaError'; data: JavaError } | { type: 'CryptoError'; data: CryptoError } | { type: 'DiscordError'; data: DiscordError } | { type: 'MetadataError'; data: MetadataError } | { type: 'ClusterError'; data: ClusterError } | { type: 'MinecraftAuthError'; data: MinecraftAuthError } | { type: 'ProcessError'; data: ProcessError } | { type: 'PackageError'; data: PackageError } | { type: 'DaoError'; data: DaoError } | { type: 'SerdeError' } | { type: 'AnyhowError' } | { type: 'DbError' } | { type: 'ReqwestError' } | { type: 'InterpulseError' } | { type: 'RegexError' } | { type: 'SemaphoreError' } | { type: 'UrlError' } | { type: 'TauriError' }; + export type LauncherEvent = { Ingress: IngressPayload } | { Message: MessagePayload } | { Process: ProcessPayload }; + export type MessageLevel = 'Info' | 'Warn' | 'Error'; + export interface MessagePayload { level: MessageLevel; message: string } + +export type MetadataError = 'FetchError' | { NotModdedManifest: GameLoader } | { NotVanillaManifest: GameLoader } | 'ParseError' | 'NoMatchingLoader' | 'NoMatchingVersion'; + +/** + * Wrapper around all `Error`s that can occur during the Microsoft authentication process. + */ +export type MinecraftAuthError = 'PublicKeyReading' | 'PKCS8Error' | { SerializeError: { step: MinecraftAuthStep } } | { DeserializeError: { step: MinecraftAuthStep; raw: string } } | { RequestError: { step: MinecraftAuthStep } } | { SigningError: { step: MinecraftAuthStep } } | 'HashError' | 'SessionIdError'; + +/** + * An ordered list of all MSA authentication steps. + */ +export type MinecraftAuthStep = 'DeviceToken' | 'SisuAuthenicate' | 'OAuthToken' | 'RefreshOAuthToken' | 'SisuAuthorize' | 'XstsAuthorize' | 'MinecraftToken' | 'MinecraftEntitlements' | 'MinecraftProfile'; + /** * A structure of all needed Minecraft credentials for logging in and account management. */ @@ -173,61 +86,29 @@ export interface MinecraftCredentials { expires: string; } + export interface Model { id: bigint; folder_name: string; stage: ClusterStage; created_at: string; group_id: bigint | null; name: string; mc_version: string; mc_loader: GameLoader; mc_loader_version: string | null; last_played: string | null; overall_played: bigint | null; icon_url: Icon | null; setting_profile_name: string | null; linked_pack_id: string | null; linked_pack_version: string | null } + +export type PackageError = 'NoPrimaryFile' | 'IsModPack' | { Incompatible: IncompatiblePackageType }; + export interface Process { pid: number; started_at: string; cluster_id: bigint; post_hook: string | null; account_id: string } + +export type ProcessError = { HookUnsuccessful: number } | 'NoPid'; + export type ProcessPayload = { Starting: { command: string } } | { Started: { process: Process } } | { Stopped: { pid: number; exit_code: number } } | { Output: { pid: number; output: string } }; + export interface Resolution { width: number; height: number } -export interface SerializableError { kind: 'CoreError' } + export interface SettingProfileModel { name: string; java_id: bigint | null; res: Resolution | null; force_fullscreen: boolean | null; mem_max: number | null; launch_args: string | null; launch_env: string | null; hook_pre: string | null; hook_wrapper: string | null; hook_post: string | null; os_extra: SettingsOsExtra | null } + export interface SettingsOsExtra { enable_gamemode: boolean | null } -interface __EventObj__ { - listen: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - once: ( - cb: TAURI_API_EVENT.EventCallback, - ) => ReturnType>; - emit: null extends T - ? (payload?: T) => ReturnType - : (payload: T) => ReturnType; +const ARGS_MAP = { 'onelauncher': '{"open_dev_tools":[],"return_error":[]}', '': '{"send_event":["event"]}', 'core': '{"get_clusters":[],"get_user":["uuid"],"remove_user":["uuid"],"create_cluster":["options"],"get_profile_or_default":["name"],"get_default_user":["fallback"],"set_default_user":["uuid"],"get_global_profile":[],"open_msa_login":[],"get_users":[],"get_cluster":["id"],"remove_cluster":["id"]}' }; +export interface Router { + 'onelauncher': { return_error: () => Promise; open_dev_tools: () => Promise }; + '': { send_event: (event: LauncherEvent) => Promise }; + 'core': { get_clusters: () => Promise>; get_cluster: (id: bigint) => Promise; remove_cluster: (id: bigint) => Promise; create_cluster: (options: CreateCluster) => Promise; get_profile_or_default: (name: string | null) => Promise; get_global_profile: () => Promise; get_users: () => Promise>; get_user: (uuid: string) => Promise; remove_user: (uuid: string) => Promise; get_default_user: (fallback: boolean | null) => Promise; set_default_user: (uuid: string | null) => Promise; open_msa_login: () => Promise }; } -export type Result = - | { status: 'ok'; data: T } - | { status: 'error'; error: E }; - -function __makeEvents__>( - mappings: Record, -) { - return new Proxy( - {} as unknown as { - [K in keyof T]: __EventObj__ & { - (handle: __WebviewWindow__): __EventObj__; - }; - }, - { - get: (_, event) => { - const name = mappings[event as keyof T]; - - return new Proxy((() => {}) as any, { - apply: (_, __, [window]: [__WebviewWindow__]) => ({ - listen: (arg: any) => window.listen(name, arg), - once: (arg: any) => window.once(name, arg), - emit: (arg: any) => window.emit(name, arg), - }), - get: (_, command: keyof __EventObj__) => { - switch (command) { - case 'listen': - return (arg: any) => TAURI_API_EVENT.listen(name, arg); - case 'once': - return (arg: any) => TAURI_API_EVENT.once(name, arg); - case 'emit': - return (arg: any) => TAURI_API_EVENT.emit(name, arg); - } - }, - }); - }, - }, - ); -} +export type { InferCommandOutput }; +export const createTauRPCProxy = () => createProxy(ARGS_MAP); diff --git a/apps/onelauncher/frontend/src/components/Navbar.tsx b/apps/onelauncher/frontend/src/components/Navbar.tsx index 645b58fe..e2d848e4 100644 --- a/apps/onelauncher/frontend/src/components/Navbar.tsx +++ b/apps/onelauncher/frontend/src/components/Navbar.tsx @@ -10,7 +10,7 @@ import PlayerHead from './content/PlayerHead'; import AccountPopup from './overlay/popups/AccountPopup'; function Navbar() { - const defaultUser = useCommand('getDefaultUser', () => bindings.commands.getDefaultUser(false)); + const defaultUser = useCommand('getDefaultUser', () => bindings.core.get_default_user(false)); return (
diff --git a/apps/onelauncher/frontend/src/hooks/useCluster.ts b/apps/onelauncher/frontend/src/hooks/useCluster.ts index 56754a99..634b1346 100644 --- a/apps/onelauncher/frontend/src/hooks/useCluster.ts +++ b/apps/onelauncher/frontend/src/hooks/useCluster.ts @@ -1,14 +1,15 @@ -import { bindings } from "@/main"; -import useCommand from "./useCommand"; -import { useEffect, useState } from "react"; -import type { Model } from "@/bindings.gen"; +import type { Model } from '@/bindings.gen'; +import { bindings } from '@/main'; +import { useEffect, useState } from 'react'; +import useCommand from './useCommand'; function useRecentCluster() { - const result = useCommand("getClusters", bindings.commands.getClusters); + const result = useCommand('getClusters', bindings.core.get_clusters); const [cluster, setCluster] = useState(); useEffect(() => { - if (!result.data) return; + if (!result.data) + return; let mostRecentCluster: Model | undefined; @@ -23,13 +24,13 @@ function useRecentCluster() { if (typeof currentPlayed !== 'string' && typeof newPlayed === 'string') { mostRecentCluster = c; - } else if (typeof currentPlayed === 'string' && typeof newPlayed === 'string') { + } + else if (typeof currentPlayed === 'string' && typeof newPlayed === 'string') { const playedAt = new Date(currentPlayed); const clusterPlayedAt = new Date(newPlayed); - if (clusterPlayedAt > playedAt) { + if (clusterPlayedAt > playedAt) mostRecentCluster = c; - } } } @@ -39,4 +40,4 @@ function useRecentCluster() { return cluster; } -export default useRecentCluster; \ No newline at end of file +export default useRecentCluster; diff --git a/apps/onelauncher/frontend/src/hooks/useCommand.ts b/apps/onelauncher/frontend/src/hooks/useCommand.ts index eb7ec725..3095ccf7 100644 --- a/apps/onelauncher/frontend/src/hooks/useCommand.ts +++ b/apps/onelauncher/frontend/src/hooks/useCommand.ts @@ -1,22 +1,19 @@ -import type { Result } from '@/bindings.gen'; import type { bindings } from '@/main'; import type { UndefinedInitialDataOptions } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query'; -type CacheKey = keyof typeof bindings.commands; +type CacheKey = (keyof typeof bindings.core) | (keyof typeof bindings.onelauncher); -function useCommand(cacheKey: CacheKey | (string & {}), command: () => Promise>, options?: Omit, 'queryKey' | 'queryFn'>) { +function useCommand(cacheKey: CacheKey | (string & {}), command: () => Promise, options?: Omit, 'queryKey' | 'queryFn'>) { return useQuery({ queryKey: [cacheKey], queryFn: async () => { try { const result = await command(); - if (result.status === 'error') - return Promise.reject(result.error); - - return result.data; + return result; } catch (e) { + console.error(e); if (e instanceof Error) return Promise.reject(e); else diff --git a/apps/onelauncher/frontend/src/main.tsx b/apps/onelauncher/frontend/src/main.tsx index 6684973d..1be1a51c 100644 --- a/apps/onelauncher/frontend/src/main.tsx +++ b/apps/onelauncher/frontend/src/main.tsx @@ -3,15 +3,17 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { createRouter, RouterProvider } from '@tanstack/react-router'; import { StrictMode } from 'react'; import ReactDOM from 'react-dom/client'; -import { routeTree } from './routeTree.gen'; +import { createTauRPCProxy } from './bindings.gen'; +import { routeTree } from './routeTree.gen'; import './fonts'; import './utils/nativeExperience'; import './utils/devExperience'; import 'overlayscrollbars/overlayscrollbars.css'; import './styles/global.css'; -export * as bindings from './bindings.gen'; +// Tauri bindings +export const bindings = createTauRPCProxy(); // Tanstack Query Client const queryClient = new QueryClient({ diff --git a/apps/onelauncher/frontend/src/routes/app/index.tsx b/apps/onelauncher/frontend/src/routes/app/index.tsx index 1fc42598..0e7d483b 100644 --- a/apps/onelauncher/frontend/src/routes/app/index.tsx +++ b/apps/onelauncher/frontend/src/routes/app/index.tsx @@ -1,15 +1,15 @@ -import DefaultBanner from '@/assets/images/default_banner.png'; import type { Model } from '@/bindings.gen'; +import DefaultBanner from '@/assets/images/default_banner.png'; +import HeaderImage from '@/assets/images/default_banner.png'; +import DefaultInstancePhoto from '@/assets/images/default_instance_cover.jpg'; import Button from '@/components/base/Button'; +import { TextField } from '@/components/base/TextField'; +import Modal from '@/components/overlay/Modal'; import useCommand from '@/hooks/useCommand'; import { bindings } from '@/main'; import { createFileRoute, Link, useNavigate } from '@tanstack/react-router'; -import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; -import DefaultInstancePhoto from "@/assets/images/default_instance_cover.jpg" -import Modal from '@/components/overlay/Modal'; import { Server01Icon } from '@untitled-theme/icons-react'; -import HeaderImage from "@/assets/images/default_banner.png"; -import { TextField } from '@/components/base/TextField'; +import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; export const Route = createFileRoute('/app/')({ component: RouteComponent, @@ -20,7 +20,11 @@ Please note this route has a very big issue related to scrolling and i am very angry rn so i will not be fixing it rn */ function RouteComponent() { - const result = useCommand("getClusters", bindings.commands.getClusters) + const result = useCommand('getClusters', bindings.core.get_clusters); + const test = useCommand('ajgiagwo', bindings.onelauncher.return_error, { + enabled: true, + retry: false, + }); return (
@@ -32,7 +36,7 @@ function RouteComponent() {
-
+
@@ -111,25 +115,23 @@ function Banner() { } function ClusterCreate() { - const result = useCommand("createCluster", () => bindings.commands.createCluster({ - icon_url: "asd", - mc_loader: "vanilla", - mc_version: "1.20.1", - name: "Test Cluster", - mc_loader_version: "0.13.5", + const result = useCommand('createCluster', () => bindings.commands.createCluster({ + icon_url: 'asd', + mc_loader: 'vanilla', + mc_version: '1.20.1', + name: 'Test Cluster', + mc_loader_version: '0.13.5', }), { enabled: false, - subscribed: false - }) + subscribed: false, + }); const testThingy = () => { - result.refetch() + result.refetch(); - if (result.isError) { - alert(result.error.message) - return; - } - } + if (result.isError) + alert(result.error.message); + }; return ( <> @@ -157,13 +159,13 @@ function ClusterCreate() {
- +
@@ -172,43 +174,44 @@ function ClusterCreate() { - ) + ); } interface ClusterGroupProps { - clusters: Model[] | undefined; + clusters: Array | undefined; isFetching?: boolean; } function ClusterGroup(props: ClusterGroupProps) { - if (props.isFetching) { + if (props.isFetching) return ( -
+
- ) - } + ); return (
-
- {props.clusters?.map((data) => ( +
+ {props.clusters?.map(data => ( ))}
- ) + ); } function ClusterCard(props: Model) { return ( - +
@@ -239,4 +242,4 @@ function ClusterCard(props: Model) {
); -} \ No newline at end of file +} diff --git a/packages/core/Cargo.toml b/packages/core/Cargo.toml index 82ac680e..2a63013e 100644 --- a/packages/core/Cargo.toml +++ b/packages/core/Cargo.toml @@ -17,7 +17,7 @@ path = "src/lib.rs" default = [ "tauri" ] tauri = [ "dep:tauri", - "dep:tauri-specta", + "dep:taurpc", "specta", "gui", "onelauncher_macro/tauri", @@ -97,7 +97,7 @@ sanitize-filename = { workspace = true } # features[tauri] tauri = { workspace = true, optional = true } -tauri-specta = { workspace = true, optional = true } +taurpc = { workspace = true, optional = true } # features[specta] specta = { workspace = true, optional = true } diff --git a/packages/core/src/api/cluster/mod.rs b/packages/core/src/api/cluster/mod.rs index 681051c4..fa9704d3 100644 --- a/packages/core/src/api/cluster/mod.rs +++ b/packages/core/src/api/cluster/mod.rs @@ -6,6 +6,7 @@ use onelauncher_entity::{clusters, java_versions}; use onelauncher_entity::icon::Icon; use onelauncher_entity::loader::GameLoader; use sea_orm::ActiveValue::Set; +use serde::Serialize; use tokio::process::Command; use crate::api::game::arguments; @@ -24,7 +25,8 @@ pub use sync::*; use super::ingress::init_ingress; -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum ClusterError { #[error("version '{0}' was not found")] InvalidVersion(String), diff --git a/packages/core/src/api/credentials.rs b/packages/core/src/api/credentials.rs index 9141acdf..e2229123 100644 --- a/packages/core/src/api/credentials.rs +++ b/packages/core/src/api/credentials.rs @@ -33,7 +33,7 @@ pub async fn get_default_user() -> LauncherResult> { #[tracing::instrument] pub async fn set_default_user(user: Option) -> LauncherResult<()> { let user = match user { - Some(user) => Some(get_user(user).await?.id), + Some(user) => get_user(user).await?.map(|user| user.id), None => None, }; @@ -63,7 +63,7 @@ pub async fn remove_user(user: uuid::Uuid) -> LauncherResult<()> { /// Get a list of user [`MinecraftCredentials`]. #[tracing::instrument] -pub async fn users() -> LauncherResult> { +pub async fn get_users() -> LauncherResult> { let state = State::get().await?; let store = state.credentials.read().await; @@ -73,15 +73,14 @@ pub async fn users() -> LauncherResult> { /// Get a specifc user's [`MinecraftCredentials`] by their [`uuid::Uuid`]. /// Use [`crate::store::MinecraftState#refresh`] instead. #[tracing::instrument] -pub async fn get_user(user: uuid::Uuid) -> LauncherResult { +pub async fn get_user(user: uuid::Uuid) -> LauncherResult> { let state = State::get().await?; let store = state.credentials.read().await; let user = store .users .get(&user) - .ok_or_else(|| anyhow::anyhow!("failed to get nonexistent user with uuid {user}"))? - .clone(); + .cloned(); Ok(user) } diff --git a/packages/core/src/api/java/mod.rs b/packages/core/src/api/java/mod.rs index 1cf5bbdf..50362ea7 100644 --- a/packages/core/src/api/java/mod.rs +++ b/packages/core/src/api/java/mod.rs @@ -12,12 +12,13 @@ use crate::utils::io; pub mod dao; -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum JavaError { #[error("failed to parse version '{0}'")] - ParseVersion(String, #[source] std::num::ParseIntError), + ParseVersion(String, #[source] #[serde(skip)] std::num::ParseIntError), #[error("failed to execute java command")] - Execute(#[from] std::io::Error), + Execute(#[from] #[serde(skip)] std::io::Error), #[error("no java installations found")] MissingJava, } diff --git a/packages/core/src/api/mod.rs b/packages/core/src/api/mod.rs index 88a43f71..3df60f90 100644 --- a/packages/core/src/api/mod.rs +++ b/packages/core/src/api/mod.rs @@ -7,3 +7,6 @@ pub mod packages; pub mod processes; pub mod proxy; pub mod setting_profiles; + +#[cfg(feature = "tauri")] +pub mod tauri; diff --git a/packages/core/src/api/packages/mod.rs b/packages/core/src/api/packages/mod.rs index 6ea9e81c..00c6e5e1 100644 --- a/packages/core/src/api/packages/mod.rs +++ b/packages/core/src/api/packages/mod.rs @@ -3,6 +3,7 @@ use onelauncher_entity::icon::Icon; use onelauncher_entity::package::PackageType; use onelauncher_entity::{clusters, packages}; use reqwest::Method; +use serde::Serialize; use crate::error::{LauncherError, LauncherResult}; use crate::store::Dirs; @@ -18,7 +19,8 @@ pub mod provider; #[cfg(test)] mod tests; -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum PackageError { #[error("no file marked for download")] NoPrimaryFile, @@ -28,7 +30,8 @@ pub enum PackageError { Incompatible(#[from] IncompatiblePackageType), } -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum IncompatiblePackageType { #[error("package is not compatible with the current version")] McVersion, diff --git a/packages/core/src/api/proxy/event.rs b/packages/core/src/api/proxy/event.rs index 6b729366..fec2d154 100644 --- a/packages/core/src/api/proxy/event.rs +++ b/packages/core/src/api/proxy/event.rs @@ -2,7 +2,7 @@ use serde::Serialize; use crate::{error::LauncherResult, store::proxy::ProxyState}; -#[onelauncher_macro::specta(event)] +#[onelauncher_macro::specta] #[derive(Debug, Clone, Serialize)] pub enum LauncherEvent { Ingress(crate::api::ingress::IngressPayload), diff --git a/packages/core/src/api/proxy/impl/proxy_tauri.rs b/packages/core/src/api/proxy/impl/proxy_tauri.rs index 62417f95..edd8b7e7 100644 --- a/packages/core/src/api/proxy/impl/proxy_tauri.rs +++ b/packages/core/src/api/proxy/impl/proxy_tauri.rs @@ -1,19 +1,20 @@ -use tauri::{AppHandle, Emitter, Manager}; -use tauri_specta::Event; +use tauri::{AppHandle, Manager}; use tracing::{error, warn}; -use crate::{api::proxy::{event::LauncherEvent, LauncherProxy, message::MessageLevel}, LauncherResult}; +use crate::{api::{proxy::{event::LauncherEvent, message::MessageLevel, LauncherProxy}, tauri::LauncherEventEmitter}, LauncherResult}; #[derive(Debug)] pub struct ProxyTauri { + emitter: LauncherEventEmitter, handle: AppHandle, } impl ProxyTauri { #[must_use] - pub const fn new(handle: AppHandle) -> Self { + pub fn new(handle: AppHandle) -> Self { Self { - handle + emitter: LauncherEventEmitter::new(handle.clone()), + handle, } } } @@ -29,9 +30,7 @@ impl LauncherProxy for ProxyTauri { } } - self.handle - .emit(LauncherEvent::NAME, event) - .map_err(Into::into) + Ok(self.emitter.send_event(event)?) } #[tracing::instrument] diff --git a/packages/core/src/api/tauri/commands.rs b/packages/core/src/api/tauri/commands.rs new file mode 100644 index 00000000..a05aca35 --- /dev/null +++ b/packages/core/src/api/tauri/commands.rs @@ -0,0 +1,167 @@ +use onelauncher_entity::{icon::Icon, loader::GameLoader}; +use tauri::{AppHandle, Runtime}; + +use crate::{api::{self, cluster::dao::ClusterId}, error::LauncherResult, store::{credentials::MinecraftCredentials, Core}}; + +use onelauncher_entity::prelude::model::*; + +#[taurpc::procedures(path = "core")] +pub trait TauriLauncherApi { + + // Clusters + async fn get_clusters() -> LauncherResult>; + async fn get_cluster(id: ClusterId) -> LauncherResult>; + async fn remove_cluster(id: ClusterId) -> LauncherResult<()>; + async fn create_cluster(options: CreateCluster) -> LauncherResult; + + // Setting Profiles + async fn get_profile_or_default(name: Option) -> LauncherResult; + async fn get_global_profile() -> SettingsProfile; + + // Users + async fn get_users() -> LauncherResult>; + async fn get_user(uuid: uuid::Uuid) -> LauncherResult>; + async fn remove_user(uuid: uuid::Uuid) -> LauncherResult<()>; + async fn get_default_user(fallback: Option) -> LauncherResult>; + async fn set_default_user(uuid: Option) -> LauncherResult<()>; + async fn open_msa_login(app_handle: AppHandle) -> LauncherResult>; + +} + + +#[derive(serde::Serialize, serde::Deserialize, specta::Type, Clone)] +pub struct CreateCluster { + name: String, + mc_version: String, + mc_loader: GameLoader, + mc_loader_version: Option, + icon: Option, +} + + +#[taurpc::ipc_type] +pub struct TauriLauncherApiImpl; + +#[taurpc::resolvers] +impl TauriLauncherApi for TauriLauncherApiImpl { + async fn get_clusters(self) -> LauncherResult> { + api::cluster::dao::get_all_clusters().await + } + + async fn get_cluster(self, id: ClusterId) -> LauncherResult> { + api::cluster::dao::get_cluster_by_id(id).await + } + + async fn remove_cluster(self, id: ClusterId) -> LauncherResult<()> { + api::cluster::dao::delete_cluster_by_id(id).await + } + + async fn create_cluster(self, options: CreateCluster) -> LauncherResult { + let cluster = api::cluster::create_cluster(&options.name, &options.mc_version, options.mc_loader, options.mc_loader_version.as_deref(), options.icon).await?; + + // if api::setting_profiles::dao::get_profile_by_name(&options.name).await?.is_none() { + // api::setting_profiles::create_profile(&options.name, async |mut profile| { + // profile.mem_max = sea_orm::ActiveValue::Set(Some(2048)); + // Ok(profile) + // }).await?; + // } + + Ok(cluster) + } + + async fn get_profile_or_default(self, name: Option) -> LauncherResult { + api::setting_profiles::dao::get_profile_or_default(name.as_ref()).await + } + + async fn get_global_profile(self) -> SettingsProfile { + api::setting_profiles::get_global_profile().await + } + + async fn get_users(self) -> LauncherResult> { + api::credentials::get_users().await + } + + async fn get_user(self, uuid: uuid::Uuid) -> LauncherResult> { + api::credentials::get_user(uuid).await + } + + async fn remove_user(self, uuid: uuid::Uuid) -> LauncherResult<()> { + api::credentials::remove_user(uuid).await + } + + async fn get_default_user(self, fallback: Option) -> LauncherResult> { + let uuid = api::credentials::get_default_user().await?; + + if fallback.is_some_and(|fallback| fallback) && uuid.is_none() { + return Ok(api::credentials::get_users().await?.first().cloned()); + } + + match uuid { + Some(uuid) => Ok(api::credentials::get_user(uuid).await?), + None => Ok(None), + } + } + + async fn set_default_user(self, uuid: Option) -> LauncherResult<()> { + api::credentials::set_default_user(uuid).await + } + + async fn open_msa_login(self, app_handle: AppHandle) -> LauncherResult> { + use tauri::Manager; + + let flow = api::credentials::begin().await?; + + let now = chrono::Utc::now(); + + if let Some(win) = app_handle.get_webview_window("login") { + win.close()?; + } + + let win = tauri::WebviewWindowBuilder::new( + &app_handle, + "login", + tauri::WebviewUrl::External( + flow.redirect_uri + .parse() + .map_err(|_| anyhow::anyhow!("failed to parse auth redirect url"))?, + ), + ) + .title(format!("Login to {}", Core::get().launcher_name)) + .center() + .focused(true) + .build()?; + + win.request_user_attention(Some(tauri::UserAttentionType::Critical))?; + + while (chrono::Utc::now() - now) < chrono::Duration::minutes(10) { + if win.title().is_err() { + return Ok(None); + } + + if win + .url()? + .as_str() + .starts_with("https://login.live.com/oauth20_desktop.srf") + { + if let Some((_, code)) = win + .url()? + .query_pairs() + .find(|x| x.0 == "code") + { + win.close()?; + let value = api::credentials::finish(&code.clone(), flow).await?; + + return Ok(Some(value)); + } + } + + tokio::time::sleep(std::time::Duration::from_millis(50)).await; + } + + win.close()?; + + Ok(None) + } + +} + diff --git a/packages/core/src/api/tauri/events.rs b/packages/core/src/api/tauri/events.rs new file mode 100644 index 00000000..6ff19fab --- /dev/null +++ b/packages/core/src/api/tauri/events.rs @@ -0,0 +1,13 @@ +use crate::api::proxy::event::LauncherEvent; + +#[taurpc::procedures(event_trigger = LauncherEventEmitter)] +pub trait TauriLauncherEventApi { + #[taurpc(event)] + async fn send_event(event: LauncherEvent); +} + +#[taurpc::ipc_type] +pub struct TauriLauncherEventApiImpl; + +#[taurpc::resolvers] +impl TauriLauncherEventApi for TauriLauncherEventApiImpl {} diff --git a/packages/core/src/api/tauri/mod.rs b/packages/core/src/api/tauri/mod.rs new file mode 100644 index 00000000..8df55897 --- /dev/null +++ b/packages/core/src/api/tauri/mod.rs @@ -0,0 +1,5 @@ +mod events; +pub use events::*; + +mod commands; +pub use commands::*; \ No newline at end of file diff --git a/packages/core/src/error.rs b/packages/core/src/error.rs index 40451c1e..d24049f4 100644 --- a/packages/core/src/error.rs +++ b/packages/core/src/error.rs @@ -1,4 +1,8 @@ -#[derive(thiserror::Error, Debug)] +use serde::Serialize; + +#[onelauncher_macro::specta] +#[derive(thiserror::Error, Debug, Serialize)] +#[serde(tag = "type", content = "data")] pub enum LauncherError { #[error(transparent)] DirError(#[from] crate::store::DirectoryError), @@ -26,30 +30,41 @@ pub enum LauncherError { DaoError(#[from] DaoError), #[error("json error: {0}")] - SerdeError(#[from] serde_json::Error), + SerdeError(#[from] #[serde(skip)] serde_json::Error), #[error(transparent)] - AnyhowError(#[from] anyhow::Error), + AnyhowError(#[from] #[serde(skip)] anyhow::Error), #[error("database error: {0}")] - DbError(#[from] sea_orm::DbErr), + DbError(#[from] #[serde(skip)] sea_orm::DbErr), #[error("http error: {0}")] - ReqwestError(#[from] reqwest::Error), + ReqwestError(#[from] #[serde(skip)] reqwest::Error), #[error("meta error: {0}")] - InterpulseError(#[from] interpulse::Error), + InterpulseError(#[from] #[serde(skip)] interpulse::Error), #[error(transparent)] - RegexError(#[from] regex::Error), + RegexError(#[from] #[serde(skip)] regex::Error), #[error("couldn't acquire semaphore: {0}")] - SemaphoreError(#[from] tokio::sync::AcquireError), + SemaphoreError(#[from] #[serde(skip)] tokio::sync::AcquireError), #[error(transparent)] - UrlError(#[from] url::ParseError), + UrlError(#[from] #[serde(skip)] url::ParseError), #[cfg(feature = "tauri")] #[error(transparent)] - TauriError(#[from] tauri::Error), + TauriError(#[from] #[serde(skip)] tauri::Error), } +// impl serde::Serialize for LauncherError { +// fn serialize(&self, serializer: S) -> Result +// where +// S: serde::Serializer, +// { +// // Serialize the error as a string +// serializer.serialize_str(&self.to_string()) +// } +// } + pub type LauncherResult = Result; -#[derive(thiserror::Error, Debug)] +#[onelauncher_macro::specta] +#[derive(thiserror::Error, Debug, Serialize)] pub enum DaoError { #[error("entity was not found")] NotFound, diff --git a/packages/core/src/store/credentials.rs b/packages/core/src/store/credentials.rs index 713917be..c32d7b73 100644 --- a/packages/core/src/store/credentials.rs +++ b/packages/core/src/store/credentials.rs @@ -1010,17 +1010,19 @@ pub enum MinecraftAuthStep { } /// Wrapper around all `Error`s that can occur during the Microsoft authentication process. -#[derive(thiserror::Error, Debug)] +#[onelauncher_macro::specta] +#[derive(thiserror::Error, Debug, Serialize)] pub enum MinecraftAuthError { #[error("failed to read public key during key generation")] PublicKeyReading, #[error("failed to serialize private key using PKCS8: {0}")] - PKCS8Error(#[from] p256::pkcs8::Error), + PKCS8Error(#[from] #[serde(skip)] p256::pkcs8::Error), #[error("failed to serialize JSON during MSA step {step:?}: {source}")] SerializeError { step: MinecraftAuthStep, #[source] + #[serde(skip)] source: serde_json::Error, }, #[error( @@ -1031,20 +1033,24 @@ pub enum MinecraftAuthError { raw: String, #[source] + #[serde(skip)] source: serde_json::Error, + #[serde(skip)] status_code: reqwest::StatusCode, }, #[error("failed to request using HTTP during MSA step {step:?}: {source}")] RequestError { step: MinecraftAuthStep, #[source] + #[serde(skip)] source: reqwest::Error, }, #[error("failed to create signed buffer during MSA step {step:?}: {source}")] SigningError { step: MinecraftAuthStep, #[source] + #[serde(skip)] source: std::io::Error, }, #[error("failed to read user hash")] diff --git a/packages/core/src/store/db.rs b/packages/core/src/store/db.rs index 3b5560a2..ab96e24e 100644 --- a/packages/core/src/store/db.rs +++ b/packages/core/src/store/db.rs @@ -3,7 +3,7 @@ use std::{path::PathBuf, time::Duration}; use onelauncher_migration::MigratorTrait; use sea_orm::{ConnectOptions, Database, DatabaseConnection}; -use crate::{utils::io::{self, IOError}, LauncherResult}; +use crate::{utils::io::IOError, LauncherResult}; use super::Dirs; diff --git a/packages/core/src/store/dirs.rs b/packages/core/src/store/dirs.rs index 2fcb540f..7300149f 100644 --- a/packages/core/src/store/dirs.rs +++ b/packages/core/src/store/dirs.rs @@ -1,6 +1,7 @@ use std::path::PathBuf; use onelauncher_entity::package::{PackageType, Provider}; +use serde::Serialize; use tokio::sync::OnceCell; use crate::{utils, LauncherResult}; @@ -15,7 +16,8 @@ pub struct Dirs { base_dir: PathBuf, } -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum DirectoryError { #[error("Failed to get the base directory for the launcher.")] BaseDir, diff --git a/packages/core/src/store/discord.rs b/packages/core/src/store/discord.rs index 02fbf04e..4c3134ce 100644 --- a/packages/core/src/store/discord.rs +++ b/packages/core/src/store/discord.rs @@ -1,6 +1,7 @@ use std::sync::{atomic::AtomicBool, Arc}; use discord_rich_presence::{activity::{Activity, Assets, Button, Timestamps}, DiscordIpc, DiscordIpcClient}; +use serde::Serialize; use tokio::sync::RwLock; use crate::error::LauncherResult; @@ -106,7 +107,8 @@ impl DiscordRPC { } } -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum DiscordError { #[error("Discord client ID is missing")] MissingClientId, diff --git a/packages/core/src/store/ingress.rs b/packages/core/src/store/ingress.rs index 0db6c539..77562965 100644 --- a/packages/core/src/store/ingress.rs +++ b/packages/core/src/store/ingress.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use serde::Serialize; +use serde::{Deserialize, Serialize}; use tokio::sync::RwLock; use uuid::Uuid; @@ -15,7 +15,8 @@ pub struct IngressProcessor { ingress_feeds: RwLock>, } -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum IngressError { #[error("ingress not found")] NotFound, @@ -113,8 +114,8 @@ pub struct Ingress { pub last_sent: f64, } -#[onelauncher_macro::specta] -#[derive(Serialize, Debug, Clone)] +// #[onelauncher_macro::specta] +#[derive(Serialize, Deserialize, specta::Type, Debug, Clone)] pub enum IngressType { Download { file_name: String }, JavaPrepare, diff --git a/packages/core/src/store/metadata.rs b/packages/core/src/store/metadata.rs index c4df95f5..228539d9 100644 --- a/packages/core/src/store/metadata.rs +++ b/packages/core/src/store/metadata.rs @@ -31,7 +31,8 @@ struct MetadataInner { // legacyfabric: Option, } -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum MetadataError { #[error("failed to fetch metadata")] FetchError, @@ -39,8 +40,8 @@ pub enum MetadataError { NotModdedManifest(GameLoader), #[error("loader {0} does not use a vanilla manifest")] NotVanillaManifest(GameLoader), - #[error("failed to parse metadata")] - ParseError(#[from] serde_json::Error), + #[error("failed to parse metadata: {0}")] + ParseError(#[from] #[serde(skip)] serde_json::Error), #[error("no matching loader found")] NoMatchingLoader, #[error("no matching version found")] diff --git a/packages/core/src/store/processes.rs b/packages/core/src/store/processes.rs index 0faf077d..c111771f 100644 --- a/packages/core/src/store/processes.rs +++ b/packages/core/src/store/processes.rs @@ -24,7 +24,8 @@ use crate::utils::io::IOError; use super::State; use super::credentials::MinecraftCredentials; -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum ProcessError { #[error("launch hook exited with non-zero code {0}")] HookUnsuccessful(i32), diff --git a/packages/core/src/utils/crypto.rs b/packages/core/src/utils/crypto.rs index 0576f8cf..8020dab4 100644 --- a/packages/core/src/utils/crypto.rs +++ b/packages/core/src/utils/crypto.rs @@ -3,11 +3,13 @@ use std::io::Read; use std::str::FromStr; use digest::DynDigest; +use serde::{Deserialize, Serialize}; use tokio::io::AsyncReadExt; use super::io::IOError; -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum CryptoError { #[error("expected {algorithm} hash {expected}, but got {actual}")] InvalidHash { @@ -19,7 +21,8 @@ pub enum CryptoError { InvalidAlgorithm, } -#[derive(Debug, Clone, PartialEq, Eq)] +#[onelauncher_macro::specta] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum HashAlgorithm { Sha1, Sha256, diff --git a/packages/core/src/utils/io.rs b/packages/core/src/utils/io.rs index fc157c8e..1342b91f 100644 --- a/packages/core/src/utils/io.rs +++ b/packages/core/src/utils/io.rs @@ -11,7 +11,8 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio_util::compat::TokioAsyncWriteCompatExt; /// A wrapper around generic and unhelpful [`std::io::Error`] messages. -#[derive(Debug, thiserror::Error)] +#[onelauncher_macro::specta] +#[derive(Debug, thiserror::Error, Serialize)] pub enum IOError { #[error("invalid absolute path '{0}'")] InvalidAbsolutePath(PathBuf), @@ -19,15 +20,16 @@ pub enum IOError { #[error("error acessing path: {source}, path: {path}")] IOErrorWrapper { #[source] + #[serde(skip)] source: std::io::Error, path: String, }, #[error(transparent)] - IOError(#[from] std::io::Error), + IOError(#[from] #[serde(skip)] std::io::Error), #[error("json deserialization error: {0}")] - DeserializeError(#[from] serde_json::Error), + DeserializeError(#[from] #[serde(skip)] serde_json::Error), #[error(transparent)] - AsyncZipError(#[from] async_zip::error::ZipError), + AsyncZipError(#[from] #[serde(skip)] async_zip::error::ZipError), } impl> From<(P, std::io::Error)> for IOError { diff --git a/packages/entity/Cargo.toml b/packages/entity/Cargo.toml index 999bbcc8..8430f58b 100644 --- a/packages/entity/Cargo.toml +++ b/packages/entity/Cargo.toml @@ -17,8 +17,8 @@ path = "src/lib.rs" [features] default = [] specta = [ - "onelauncher_macro/specta", "dep:specta", + "onelauncher_macro/specta", "interpulse/specta" ] @@ -33,3 +33,6 @@ url = { workspace = true } interpulse = { workspace = true } merge = { workspace = true } specta = { workspace = true, optional = true } + +[lints] +workspace = true diff --git a/packages/entity/src/domain/settings.rs b/packages/entity/src/domain/settings.rs index 156972a1..ed8c337a 100644 --- a/packages/entity/src/domain/settings.rs +++ b/packages/entity/src/domain/settings.rs @@ -1,7 +1,7 @@ use sea_orm::{sea_query::{self, Nullable, ValueType}, TryGetableFromJson}; use serde::{Deserialize, Serialize}; -cfg_match! { +cfg_select! { target_os = "windows" => { #[onelauncher_macro::specta] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] diff --git a/packages/entity/src/lib.rs b/packages/entity/src/lib.rs index 46eabd8d..73f3c8cf 100644 --- a/packages/entity/src/lib.rs +++ b/packages/entity/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(cfg_match)] +#![feature(cfg_select)] mod entities; mod domain; diff --git a/packages/macro/Cargo.toml b/packages/macro/Cargo.toml index 70f14c98..fdacd467 100644 --- a/packages/macro/Cargo.toml +++ b/packages/macro/Cargo.toml @@ -24,3 +24,6 @@ specta = [] syn = { workspace = true } quote = { workspace = true } proc-macro2 = { workspace = true } + +[lints] +workspace = true diff --git a/packages/macro/src/specta.rs b/packages/macro/src/specta.rs index 3529898b..9eb2aa2f 100644 --- a/packages/macro/src/specta.rs +++ b/packages/macro/src/specta.rs @@ -1,3 +1,4 @@ +use proc_macro2::TokenStream; use quote::quote; use syn::{meta::ParseNestedMeta, parse_macro_input, Item}; @@ -8,28 +9,17 @@ pub fn specta(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> p let input = parse_macro_input!(item as Item); - let specta_event_type = if attrs.event { - quote! { - #[cfg_attr(feature = "tauri", derive(tauri_specta::Event))] - } - } else { - proc_macro2::TokenStream::new() - }; - - let specta_type = if let Some(rename) = attrs.rename { + let specta_rename = if let Some(rename) = attrs.rename { quote! { - #[cfg_attr(feature = "specta", derive(specta::Type))] #[cfg_attr(feature = "specta", specta(rename = #rename))] } } else { - quote! { - #[cfg_attr(feature = "specta", derive(specta::Type))] - } + TokenStream::new() }; let expanded = quote! { - #specta_event_type - #specta_type + #[cfg_attr(feature = "specta", derive(specta::Type))] + #specta_rename #input }; @@ -38,15 +28,12 @@ pub fn specta(attr: proc_macro::TokenStream, item: proc_macro::TokenStream) -> p #[derive(Default)] struct SpectaAttributes { - event: bool, rename: Option, } impl SpectaAttributes { fn parse(&mut self, meta: ParseNestedMeta) -> syn::Result<()> { - if meta.path.is_ident("event") { - self.event = true; - } else if meta.path.is_ident("rename") { + if meta.path.is_ident("rename") { self.rename = Some(meta.value()?.to_string()); } Ok(()) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de577ba4..dfef8c1f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,6 +73,16 @@ importers: specifier: ^0.38.2 version: 0.38.2 + apps/oneclient/desktop: + dependencies: + '@oneclient/frontend': + specifier: workspace:* + version: link:../frontend + devDependencies: + '@tauri-apps/cli': + specifier: ^2.0.4 + version: 2.5.0 + apps/oneclient/frontend: dependencies: '@fontsource/poppins': @@ -263,6 +273,9 @@ importers: tailwindcss-react-aria-components: specifier: ^2.0.0 version: 2.0.0(tailwindcss@4.1.5) + taurpc: + specifier: ^1.8.1 + version: 1.8.1 devDependencies: '@tanstack/eslint-config': specifier: ^0.1.0 @@ -4462,6 +4475,9 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + taurpc@1.8.1: + resolution: {integrity: sha512-qfR1ekhXApjbnWAqyE5qpDa/oMmKBwjNNDuqjYn7sO+ChI5qwQ4J/fS+00dVLE3I0lbZDizyY8ObSFBz3MRerQ==} + text-decoder@1.1.1: resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} @@ -9934,6 +9950,10 @@ snapshots: fast-fifo: 1.3.2 streamx: 2.19.0 + taurpc@1.8.1: + dependencies: + '@tauri-apps/api': 2.5.0 + text-decoder@1.1.1: dependencies: b4a: 1.6.6