From 1a103de2f1ef75cd73347953cbe27e14606df871 Mon Sep 17 00:00:00 2001 From: 苏向夜 Date: Wed, 18 Feb 2026 15:08:40 +0800 Subject: refactor(client): rewrite macros to generate client --- src-tauri/Cargo.toml | 5 +- src-tauri/src/core/account_storage.rs | 20 ++---- src-tauri/src/core/assistant.rs | 30 ++++----- src-tauri/src/core/auth.rs | 16 ++--- src-tauri/src/core/config.rs | 15 +---- src-tauri/src/core/downloader.rs | 37 +++-------- src-tauri/src/core/fabric.rs | 54 ++++------------ src-tauri/src/core/forge.rs | 10 +-- src-tauri/src/core/game_version.rs | 50 +++------------ src-tauri/src/core/instance.rs | 15 +---- src-tauri/src/core/java/mod.rs | 20 ++---- src-tauri/src/core/java/persistence.rs | 5 +- src-tauri/src/core/java/priority.rs | 2 +- src-tauri/src/core/java/providers/adoptium.rs | 25 ++------ src-tauri/src/core/manifest.rs | 15 +---- src-tauri/src/main.rs | 85 ++++++++++++++++++++++--- src-tauri/src/utils/api.rs | 90 +++++++++++++++++++++++++++ src-tauri/src/utils/mod.rs | 2 + 18 files changed, 247 insertions(+), 249 deletions(-) create mode 100644 src-tauri/src/utils/api.rs (limited to 'src-tauri') diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 28d600a..38ef140 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -19,7 +19,6 @@ assets = [ bytes = "1.11.0" chrono = "0.4" dirs = "5.0" -dropout-core = { path = "../crates/core", version = "0.1.0" } dropout-macros = { path = "../crates/macros", version = "0.1.0" } env_logger = "0.9" flate2 = "1.0" @@ -49,5 +48,9 @@ ts-rs = { version = "11.1.0", features = ["serde-compat"] } uuid = { version = "1.10.0", features = ["serde", "v3", "v4"] } zip = "2.2.2" +[dev-dependencies] +ctor = "0.6.3" +inventory = "0.3.21" + [build-dependencies] tauri-build = { version = "2.0", features = [] } diff --git a/src-tauri/src/core/account_storage.rs b/src-tauri/src/core/account_storage.rs index a18b5fc..df202cd 100644 --- a/src-tauri/src/core/account_storage.rs +++ b/src-tauri/src/core/account_storage.rs @@ -6,10 +6,7 @@ use ts_rs::TS; /// Stored account data for persistence #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/account_storage.ts" -)] +#[ts(export, export_to = "account.ts")] pub struct AccountStore { pub accounts: Vec, pub active_account_id: Option, @@ -17,10 +14,7 @@ pub struct AccountStore { #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(tag = "type")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/account_storage.ts" -)] +#[ts(export, export_to = "account.ts")] pub enum StoredAccount { Offline(OfflineAccount), Microsoft(StoredMicrosoftAccount), @@ -28,10 +22,7 @@ pub enum StoredAccount { /// Microsoft account with refresh token for persistence #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/account_storage.ts" -)] +#[ts(export, export_to = "account.ts")] pub struct StoredMicrosoftAccount { pub username: String, pub uuid: String, @@ -78,10 +69,7 @@ impl StoredAccount { } #[derive(Debug, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/account_storage.ts" -)] +#[ts(export, export_to = "account.ts")] pub struct AccountStorage { file_path: PathBuf, } diff --git a/src-tauri/src/core/assistant.rs b/src-tauri/src/core/assistant.rs index 6e656dc..5663007 100644 --- a/src-tauri/src/core/assistant.rs +++ b/src-tauri/src/core/assistant.rs @@ -8,10 +8,7 @@ use ts_rs::TS; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/assistant.ts" -)] +#[ts(export, export_to = "assistant.ts")] pub struct Message { pub role: String, pub content: String, @@ -59,10 +56,7 @@ pub struct OllamaTagsResponse { // Simplified model info for frontend #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/assistant.ts" -)] +#[ts(export, export_to = "assistant.ts")] pub struct ModelInfo { pub id: String, pub name: String, @@ -115,10 +109,7 @@ pub struct OpenAIModelsResponse { // Streaming response structures #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/assistant.ts" -)] +#[ts(export, export_to = "assistant.ts")] pub struct GenerationStats { pub total_duration: u64, pub load_duration: u64, @@ -130,10 +121,7 @@ pub struct GenerationStats { #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/assistant.ts" -)] +#[ts(export, export_to = "assistant.ts")] pub struct StreamChunk { pub content: String, pub done: bool, @@ -244,7 +232,10 @@ impl GameAssistant { // Add language instruction if not auto if config.response_language != "auto" { - system_content = format!("{}\n\nIMPORTANT: Respond in {}. Do not include Pinyin or English translations unless explicitly requested.", system_content, config.response_language); + system_content = format!( + "{}\n\nIMPORTANT: Respond in {}. Do not include Pinyin or English translations unless explicitly requested.", + system_content, config.response_language + ); } // Add log context if available @@ -456,7 +447,10 @@ impl GameAssistant { let mut system_content = config.system_prompt.clone(); if config.response_language != "auto" { - system_content = format!("{}\n\nIMPORTANT: Respond in {}. Do not include Pinyin or English translations unless explicitly requested.", system_content, config.response_language); + system_content = format!( + "{}\n\nIMPORTANT: Respond in {}. Do not include Pinyin or English translations unless explicitly requested.", + system_content, config.response_language + ); } if !context.is_empty() { diff --git a/src-tauri/src/core/auth.rs b/src-tauri/src/core/auth.rs index 0e873e3..b137957 100644 --- a/src-tauri/src/core/auth.rs +++ b/src-tauri/src/core/auth.rs @@ -15,11 +15,7 @@ fn get_client() -> reqwest::Client { #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(tag = "type")] #[serde(rename_all = "camelCase")] -#[ts( - export, - tag = "type", - export_to = "../../packages/ui-new/src/types/bindings/auth.ts" -)] +#[ts(export, tag = "type", export_to = "auth.ts")] pub enum Account { Offline(OfflineAccount), Microsoft(MicrosoftAccount), @@ -50,7 +46,7 @@ impl Account { #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../packages/ui-new/src/types/bindings/auth.ts")] +#[ts(export, export_to = "auth.ts")] pub struct OfflineAccount { pub username: String, pub uuid: String, @@ -58,7 +54,7 @@ pub struct OfflineAccount { #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../packages/ui-new/src/types/bindings/auth.ts")] +#[ts(export, export_to = "auth.ts")] pub struct MicrosoftAccount { pub username: String, pub uuid: String, @@ -89,7 +85,7 @@ const SCOPE: &str = "XboxLive.SignIn XboxLive.offline_access"; #[derive(Debug, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../packages/ui-new/src/types/bindings/auth.ts")] +#[ts(export, export_to = "auth.ts")] pub struct DeviceCodeResponse { pub user_code: String, pub device_code: String, @@ -101,7 +97,7 @@ pub struct DeviceCodeResponse { #[derive(Debug, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../packages/ui-new/src/types/bindings/auth.ts")] +#[ts(export, export_to = "auth.ts")] pub struct TokenResponse { pub access_token: String, pub refresh_token: Option, @@ -225,7 +221,7 @@ pub struct MinecraftAuthResponse { #[derive(Debug, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../packages/ui-new/src/types/bindings/auth.ts")] +#[ts(export, export_to = "auth.ts")] pub struct MinecraftProfile { pub id: String, pub name: String, diff --git a/src-tauri/src/core/config.rs b/src-tauri/src/core/config.rs index 0d0e8ff..d1f306a 100644 --- a/src-tauri/src/core/config.rs +++ b/src-tauri/src/core/config.rs @@ -7,10 +7,7 @@ use ts_rs::TS; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/config.ts" -)] +#[ts(export, export_to = "config.ts")] #[serde(default)] pub struct AssistantConfig { pub enabled: bool, @@ -51,10 +48,7 @@ impl Default for AssistantConfig { /// Feature-gated arguments configuration #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/config.ts" -)] +#[ts(export, export_to = "config.ts")] #[serde(default)] pub struct FeatureFlags { /// Demo user: enables demo-related arguments when rules require it @@ -83,10 +77,7 @@ impl Default for FeatureFlags { #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/config.ts" -)] +#[ts(export, export_to = "config.ts")] #[serde(default)] pub struct LauncherConfig { pub min_memory: u32, // in MB diff --git a/src-tauri/src/core/downloader.rs b/src-tauri/src/core/downloader.rs index d4fc782..a6b11c5 100644 --- a/src-tauri/src/core/downloader.rs +++ b/src-tauri/src/core/downloader.rs @@ -2,8 +2,8 @@ use futures::StreamExt; use serde::{Deserialize, Serialize}; use sha1::Digest as Sha1Digest; use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; use tauri::{AppHandle, Emitter, Manager, Window}; use tokio::io::{AsyncSeekExt, AsyncWriteExt}; use tokio::sync::Semaphore; @@ -11,10 +11,7 @@ use ts_rs::TS; #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct DownloadTask { pub url: String, pub path: PathBuf, @@ -27,10 +24,7 @@ pub struct DownloadTask { /// Metadata for resumable downloads stored in .part.meta file #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct DownloadMetadata { pub url: String, pub file_name: String, @@ -44,10 +38,7 @@ pub struct DownloadMetadata { /// A download segment for multi-segment parallel downloading #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct DownloadSegment { pub start: u64, pub end: u64, @@ -58,10 +49,7 @@ pub struct DownloadSegment { /// Progress event for Java download #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct JavaDownloadProgress { pub file_name: String, pub downloaded_bytes: u64, @@ -75,10 +63,7 @@ pub struct JavaDownloadProgress { /// Pending download task for queue persistence #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct PendingJavaDownload { pub major_version: u32, pub image_type: String, @@ -93,10 +78,7 @@ pub struct PendingJavaDownload { /// Download queue for persistence #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct DownloadQueue { pub pending_downloads: Vec, } @@ -452,10 +434,7 @@ fn create_new_metadata( #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui/src/types/generated/downloader.ts" -)] +#[ts(export, export_to = "downloader.ts")] pub struct ProgressEvent { pub file: String, pub downloaded: u64, diff --git a/src-tauri/src/core/fabric.rs b/src-tauri/src/core/fabric.rs index 7385850..a6ef236 100644 --- a/src-tauri/src/core/fabric.rs +++ b/src-tauri/src/core/fabric.rs @@ -15,10 +15,7 @@ const FABRIC_META_URL: &str = "https://meta.fabricmc.net/v2"; /// Represents a Fabric loader version from the Meta API. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricLoaderVersion { pub separator: String, pub build: i32, @@ -30,10 +27,7 @@ pub struct FabricLoaderVersion { /// Represents a Fabric intermediary mapping version. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricIntermediaryVersion { pub maven: String, pub version: String, @@ -43,10 +37,7 @@ pub struct FabricIntermediaryVersion { /// Represents a combined loader + intermediary version entry. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricLoaderEntry { pub loader: FabricLoaderVersion, pub intermediary: FabricIntermediaryVersion, @@ -57,10 +48,7 @@ pub struct FabricLoaderEntry { /// Launcher metadata from Fabric Meta API. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricLauncherMeta { pub version: i32, pub libraries: FabricLibraries, @@ -71,10 +59,7 @@ pub struct FabricLauncherMeta { /// Libraries required by Fabric loader. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricLibraries { pub client: Vec, pub common: Vec, @@ -84,10 +69,7 @@ pub struct FabricLibraries { /// A single Fabric library dependency. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricLibrary { pub name: String, pub url: Option, @@ -97,11 +79,7 @@ pub struct FabricLibrary { /// Can be either a struct with client/server fields or a simple string. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts", - untagged -)] +#[ts(export, export_to = "fabric.ts", untagged)] #[serde(untagged)] pub enum FabricMainClass { Structured { client: String, server: String }, @@ -128,10 +106,7 @@ impl FabricMainClass { /// Represents a Minecraft version supported by Fabric. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct FabricGameVersion { pub version: String, pub stable: bool, @@ -140,10 +115,7 @@ pub struct FabricGameVersion { /// Information about an installed Fabric version. #[derive(Debug, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/fabric.ts" -)] +#[ts(export, export_to = "fabric.ts")] pub struct InstalledFabricVersion { pub id: String, pub minecraft_version: String, @@ -155,8 +127,8 @@ pub struct InstalledFabricVersion { /// /// # Returns /// A list of game versions that have Fabric intermediary mappings available. -pub async fn fetch_supported_game_versions( -) -> Result, Box> { +pub async fn fetch_supported_game_versions() +-> Result, Box> { let url = format!("{}/versions/game", FABRIC_META_URL); let resp = reqwest::get(&url) .await? @@ -169,8 +141,8 @@ pub async fn fetch_supported_game_versions( /// /// # Returns /// A list of all Fabric loader versions, ordered by build number (newest first). -pub async fn fetch_loader_versions( -) -> Result, Box> { +pub async fn fetch_loader_versions() +-> Result, Box> { let url = format!("{}/versions/loader", FABRIC_META_URL); let resp = reqwest::get(&url) .await? diff --git a/src-tauri/src/core/forge.rs b/src-tauri/src/core/forge.rs index 1d4ae1d..4452f8e 100644 --- a/src-tauri/src/core/forge.rs +++ b/src-tauri/src/core/forge.rs @@ -22,10 +22,7 @@ const FORGE_FILES_URL: &str = "https://files.minecraftforge.net/"; /// Represents a Forge version entry. #[derive(Debug, Deserialize, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/forge.ts" -)] +#[ts(export, export_to = "forge.ts")] pub struct ForgeVersion { pub version: String, pub minecraft_version: String, @@ -44,10 +41,7 @@ struct ForgePromotions { /// Information about an installed Forge version. #[derive(Debug, Serialize, Clone, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/forge.ts" -)] +#[ts(export, export_to = "forge.ts")] pub struct InstalledForgeVersion { pub id: String, pub minecraft_version: String, diff --git a/src-tauri/src/core/game_version.rs b/src-tauri/src/core/game_version.rs index 7df631a..82196bd 100644 --- a/src-tauri/src/core/game_version.rs +++ b/src-tauri/src/core/game_version.rs @@ -4,10 +4,7 @@ use ts_rs::TS; /// Represents a Minecraft version JSON, supporting both vanilla and modded (Fabric/Forge) formats. /// Modded versions use `inheritsFrom` to reference a parent vanilla version. #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct GameVersion { pub id: String, /// Optional for mod loaders that inherit from vanilla @@ -34,20 +31,14 @@ pub struct GameVersion { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct Downloads { pub client: DownloadArtifact, pub server: Option, } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct DownloadArtifact { pub sha1: Option, pub size: Option, @@ -56,10 +47,7 @@ pub struct DownloadArtifact { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct AssetIndex { pub id: String, pub sha1: String, @@ -70,10 +58,7 @@ pub struct AssetIndex { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct Library { pub downloads: Option, pub name: String, @@ -85,10 +70,7 @@ pub struct Library { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct Rule { pub action: String, // "allow" or "disallow" pub os: Option, @@ -97,10 +79,7 @@ pub struct Rule { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct OsRule { pub name: Option, // "linux", "osx", "windows" pub version: Option, // Regex @@ -108,10 +87,7 @@ pub struct OsRule { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct LibraryDownloads { pub artifact: Option, #[ts(type = "Record")] @@ -119,10 +95,7 @@ pub struct LibraryDownloads { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct Arguments { #[ts(type = "Record")] pub game: Option, @@ -131,10 +104,7 @@ pub struct Arguments { } #[derive(Debug, Deserialize, Serialize, Clone, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/game_version.ts" -)] +#[ts(export, export_to = "game-version.ts")] pub struct JavaVersion { pub component: String, #[serde(rename = "majorVersion")] diff --git a/src-tauri/src/core/instance.rs b/src-tauri/src/core/instance.rs index e842ec9..0237270 100644 --- a/src-tauri/src/core/instance.rs +++ b/src-tauri/src/core/instance.rs @@ -16,10 +16,7 @@ use ts_rs::TS; /// Represents a game instance/profile #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/instance.ts" -)] +#[ts(export, export_to = "instance.ts")] pub struct Instance { pub id: String, // 唯一标识符(UUID) pub name: String, // 显示名称 @@ -40,10 +37,7 @@ pub struct Instance { /// Memory settings override for an instance #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/instance.ts" -)] +#[ts(export, export_to = "instance.ts")] pub struct MemoryOverride { pub min: u32, // MB pub max: u32, // MB @@ -52,10 +46,7 @@ pub struct MemoryOverride { /// Configuration for all instances #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/instance.ts" -)] +#[ts(export, export_to = "instance.ts")] pub struct InstanceConfig { pub instances: Vec, pub active_instance_id: Option, // 当前活动的实例ID diff --git a/src-tauri/src/core/java/mod.rs b/src-tauri/src/core/java/mod.rs index 2215872..c8d936d 100644 --- a/src-tauri/src/core/java/mod.rs +++ b/src-tauri/src/core/java/mod.rs @@ -33,10 +33,7 @@ use providers::AdoptiumProvider; const CACHE_DURATION_SECS: u64 = 24 * 60 * 60; #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/index.ts" -)] +#[ts(export, export_to = "java/core.ts")] pub struct JavaInstallation { pub path: String, pub version: String, @@ -69,10 +66,7 @@ impl std::fmt::Display for ImageType { } #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/index.ts" -)] +#[ts(export, export_to = "java/core.ts")] pub struct JavaReleaseInfo { pub major_version: u32, pub image_type: String, @@ -88,10 +82,7 @@ pub struct JavaReleaseInfo { } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/index.ts" -)] +#[ts(export, export_to = "java/core.ts")] pub struct JavaCatalog { pub releases: Vec, pub available_major_versions: Vec, @@ -100,10 +91,7 @@ pub struct JavaCatalog { } #[derive(Debug, Clone, Serialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/index.ts" -)] +#[ts(export, export_to = "java/core.ts")] pub struct JavaDownloadInfo { pub version: String, // e.g., "17.0.2+8" pub release_name: String, // e.g., "jdk-17.0.2+8" diff --git a/src-tauri/src/core/java/persistence.rs b/src-tauri/src/core/java/persistence.rs index 6720696..a6727d7 100644 --- a/src-tauri/src/core/java/persistence.rs +++ b/src-tauri/src/core/java/persistence.rs @@ -5,10 +5,7 @@ use tauri::{AppHandle, Manager}; use ts_rs::TS; #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/persistence.ts" -)] +#[ts(export, export_to = "java/persistence.ts")] pub struct JavaConfig { pub user_defined_paths: Vec, pub preferred_java_path: Option, diff --git a/src-tauri/src/core/java/priority.rs b/src-tauri/src/core/java/priority.rs index b2e9fb4..f991eb7 100644 --- a/src-tauri/src/core/java/priority.rs +++ b/src-tauri/src/core/java/priority.rs @@ -1,8 +1,8 @@ use tauri::AppHandle; +use crate::core::java::JavaInstallation; use crate::core::java::persistence; use crate::core::java::validation; -use crate::core::java::JavaInstallation; pub async fn resolve_java_for_launch( app_handle: &AppHandle, diff --git a/src-tauri/src/core/java/providers/adoptium.rs b/src-tauri/src/core/java/providers/adoptium.rs index 20fb2d5..1765a99 100644 --- a/src-tauri/src/core/java/providers/adoptium.rs +++ b/src-tauri/src/core/java/providers/adoptium.rs @@ -9,10 +9,7 @@ use ts_rs::TS; const ADOPTIUM_API_BASE: &str = "https://api.adoptium.net/v3"; #[derive(Debug, Clone, Deserialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/providers/adoptium.ts" -)] +#[ts(export, export_to = "java/providers/adoptium.ts")] pub struct AdoptiumAsset { pub binary: AdoptiumBinary, pub release_name: String, @@ -21,10 +18,7 @@ pub struct AdoptiumAsset { #[derive(Debug, Clone, Deserialize, TS)] #[allow(dead_code)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/providers/adoptium.ts" -)] +#[ts(export, export_to = "java/providers/adoptium.ts")] pub struct AdoptiumBinary { pub os: String, pub architecture: String, @@ -35,10 +29,7 @@ pub struct AdoptiumBinary { } #[derive(Debug, Clone, Deserialize, TS)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/providers/adoptium.ts" -)] +#[ts(export, export_to = "java/providers/adoptium.ts")] pub struct AdoptiumPackage { pub name: String, pub link: String, @@ -48,10 +39,7 @@ pub struct AdoptiumPackage { #[derive(Debug, Clone, Deserialize, TS)] #[allow(dead_code)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/providers/adoptium.ts" -)] +#[ts(export, export_to = "java/providers/adoptium.ts")] pub struct AdoptiumVersionData { pub major: u32, pub minor: u32, @@ -62,10 +50,7 @@ pub struct AdoptiumVersionData { #[derive(Debug, Clone, Deserialize, TS)] #[allow(dead_code)] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/java/providers/adoptium.ts" -)] +#[ts(export, export_to = "java/providers/adoptium.ts")] pub struct AvailableReleases { pub available_releases: Vec, pub available_lts_releases: Vec, diff --git a/src-tauri/src/core/manifest.rs b/src-tauri/src/core/manifest.rs index 9e4cb4e..d40d958 100644 --- a/src-tauri/src/core/manifest.rs +++ b/src-tauri/src/core/manifest.rs @@ -7,10 +7,7 @@ use ts_rs::TS; #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/manifest.ts" -)] +#[ts(export, export_to = "manifest.ts")] pub struct VersionManifest { pub latest: Latest, pub versions: Vec, @@ -18,10 +15,7 @@ pub struct VersionManifest { #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/manifest.ts" -)] +#[ts(export, export_to = "manifest.ts")] pub struct Latest { pub release: String, pub snapshot: String, @@ -29,10 +23,7 @@ pub struct Latest { #[derive(Debug, Deserialize, Serialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../packages/ui-new/src/types/bindings/manifest.ts" -)] +#[ts(export, export_to = "manifest.ts")] pub struct Version { pub id: String, #[serde(rename = "type")] diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f652012..e51d49f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -6,7 +6,8 @@ use std::process::Stdio; use std::sync::Mutex; use tauri::{Emitter, Manager, State, Window}; // Added Emitter use tokio::io::{AsyncBufReadExt, BufReader}; -use tokio::process::Command; // Added Serialize +use tokio::process::Command; +use ts_rs::TS; // Added Serialize #[cfg(target_os = "windows")] use std::os::windows::process::CommandExt; @@ -63,6 +64,7 @@ fn has_unresolved_placeholder(s: &str) -> bool { } #[tauri::command] +#[dropout_macros::api] async fn start_game( window: Window, auth_state: State<'_, core::auth::AccountState>, @@ -941,6 +943,7 @@ async fn get_versions( /// Check if a version is installed (has client.jar) #[tauri::command] +#[dropout_macros::api] async fn check_version_installed( _window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -980,6 +983,7 @@ async fn check_version_installed( /// Install a version (download client, libraries, assets) without launching #[tauri::command] +#[dropout_macros::api] async fn install_version( window: Window, config_state: State<'_, core::config::ConfigState>, @@ -1303,6 +1307,7 @@ async fn install_version( } #[tauri::command] +#[dropout_macros::api] async fn login_offline( window: Window, state: State<'_, core::auth::AccountState>, @@ -1326,6 +1331,7 @@ async fn login_offline( } #[tauri::command] +#[dropout_macros::api] async fn get_active_account( state: State<'_, core::auth::AccountState>, ) -> Result, String> { @@ -1333,6 +1339,7 @@ async fn get_active_account( } #[tauri::command] +#[dropout_macros::api] async fn logout(window: Window, state: State<'_, core::auth::AccountState>) -> Result<(), String> { // Get current account UUID before clearing let uuid = state @@ -1359,6 +1366,7 @@ async fn logout(window: Window, state: State<'_, core::auth::AccountState>) -> R } #[tauri::command] +#[dropout_macros::api] async fn get_settings( state: State<'_, core::config::ConfigState>, ) -> Result { @@ -1366,6 +1374,7 @@ async fn get_settings( } #[tauri::command] +#[dropout_macros::api] async fn save_settings( state: State<'_, core::config::ConfigState>, config: core::config::LauncherConfig, @@ -1376,11 +1385,13 @@ async fn save_settings( } #[tauri::command] +#[dropout_macros::api] async fn get_config_path(state: State<'_, core::config::ConfigState>) -> Result { Ok(state.file_path.to_string_lossy().to_string()) } #[tauri::command] +#[dropout_macros::api] async fn read_raw_config(state: State<'_, core::config::ConfigState>) -> Result { tokio::fs::read_to_string(&state.file_path) .await @@ -1388,6 +1399,7 @@ async fn read_raw_config(state: State<'_, core::config::ConfigState>) -> Result< } #[tauri::command] +#[dropout_macros::api] async fn save_raw_config( state: State<'_, core::config::ConfigState>, content: String, @@ -1408,11 +1420,13 @@ async fn save_raw_config( } #[tauri::command] +#[dropout_macros::api] async fn start_microsoft_login() -> Result { core::auth::start_device_flow().await } #[tauri::command] +#[dropout_macros::api] async fn complete_microsoft_login( window: Window, state: State<'_, core::auth::AccountState>, @@ -1483,6 +1497,7 @@ async fn complete_microsoft_login( /// Refresh token for current Microsoft account #[tauri::command] +#[dropout_macros::api] async fn refresh_account( window: Window, state: State<'_, core::auth::AccountState>, @@ -1518,6 +1533,7 @@ async fn refresh_account( /// Detect Java installations on the system #[tauri::command] +#[dropout_macros::api] async fn detect_all_java_installations( app_handle: tauri::AppHandle, ) -> Result, String> { @@ -1526,6 +1542,7 @@ async fn detect_all_java_installations( /// Alias for detect_all_java_installations (for backward compatibility) #[tauri::command] +#[dropout_macros::api] async fn detect_java( app_handle: tauri::AppHandle, ) -> Result, String> { @@ -1534,6 +1551,7 @@ async fn detect_java( /// Get recommended Java for a specific Minecraft version #[tauri::command] +#[dropout_macros::api] async fn get_recommended_java( required_major_version: Option, ) -> Result, String> { @@ -1542,6 +1560,7 @@ async fn get_recommended_java( /// Get Adoptium Java download info #[tauri::command] +#[dropout_macros::api] async fn fetch_adoptium_java( major_version: u32, image_type: String, @@ -1557,6 +1576,7 @@ async fn fetch_adoptium_java( /// Download and install Adoptium Java #[tauri::command] +#[dropout_macros::api] async fn download_adoptium_java( app_handle: tauri::AppHandle, major_version: u32, @@ -1575,6 +1595,7 @@ async fn download_adoptium_java( /// Get available Adoptium Java versions #[tauri::command] +#[dropout_macros::api] async fn fetch_available_java_versions() -> Result, String> { core::java::fetch_available_versions() .await @@ -1583,6 +1604,7 @@ async fn fetch_available_java_versions() -> Result, String> { /// Fetch Java catalog with platform availability (uses cache) #[tauri::command] +#[dropout_macros::api] async fn fetch_java_catalog( app_handle: tauri::AppHandle, ) -> Result { @@ -1593,6 +1615,7 @@ async fn fetch_java_catalog( /// Refresh Java catalog (bypass cache) #[tauri::command] +#[dropout_macros::api] async fn refresh_java_catalog( app_handle: tauri::AppHandle, ) -> Result { @@ -1603,6 +1626,7 @@ async fn refresh_java_catalog( /// Cancel current Java download #[tauri::command] +#[dropout_macros::api] async fn cancel_java_download() -> Result<(), String> { core::java::cancel_current_download(); Ok(()) @@ -1610,6 +1634,7 @@ async fn cancel_java_download() -> Result<(), String> { /// Get pending Java downloads #[tauri::command] +#[dropout_macros::api] async fn get_pending_java_downloads( app_handle: tauri::AppHandle, ) -> Result, String> { @@ -1618,6 +1643,7 @@ async fn get_pending_java_downloads( /// Resume pending Java downloads #[tauri::command] +#[dropout_macros::api] async fn resume_java_downloads( app_handle: tauri::AppHandle, ) -> Result, String> { @@ -1626,6 +1652,7 @@ async fn resume_java_downloads( /// Get Minecraft versions supported by Fabric #[tauri::command] +#[dropout_macros::api] async fn get_fabric_game_versions() -> Result, String> { core::fabric::fetch_supported_game_versions() .await @@ -1634,6 +1661,7 @@ async fn get_fabric_game_versions() -> Result Result, String> { core::fabric::fetch_loader_versions() .await @@ -1642,6 +1670,7 @@ async fn get_fabric_loader_versions() -> Result Result, String> { @@ -1652,6 +1681,7 @@ async fn get_fabric_loaders_for_version( /// Install Fabric loader for a specific Minecraft version #[tauri::command] +#[dropout_macros::api] async fn install_fabric( window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1696,6 +1726,7 @@ async fn install_fabric( /// List installed Fabric versions #[tauri::command] +#[dropout_macros::api] async fn list_installed_fabric_versions( _window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1712,6 +1743,7 @@ async fn list_installed_fabric_versions( /// Get Java version requirement for a specific version #[tauri::command] +#[dropout_macros::api] async fn get_version_java_version( _window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1730,17 +1762,18 @@ async fn get_version_java_version( } /// Version metadata for display in the UI -#[derive(serde::Serialize)] +#[derive(serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "core.ts")] struct VersionMetadata { id: String, - #[serde(rename = "javaVersion")] java_version: Option, - #[serde(rename = "isInstalled")] is_installed: bool, } /// Delete a version (remove version directory) #[tauri::command] +#[dropout_macros::api] async fn delete_version( window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1795,6 +1828,7 @@ async fn delete_version( /// Get detailed metadata for a specific version #[tauri::command] +#[dropout_macros::api] async fn get_version_metadata( _window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1880,7 +1914,9 @@ async fn get_version_metadata( } /// Installed version info -#[derive(serde::Serialize)] +#[derive(serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "core.ts")] struct InstalledVersion { id: String, #[serde(rename = "type")] @@ -1890,6 +1926,7 @@ struct InstalledVersion { /// List all installed versions from the data directory /// Simply lists all folders in the versions directory without validation #[tauri::command] +#[dropout_macros::api] async fn list_installed_versions( _window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1976,6 +2013,7 @@ async fn list_installed_versions( /// Check if Fabric is installed for a specific version #[tauri::command] +#[dropout_macros::api] async fn is_fabric_installed( _window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -1996,6 +2034,7 @@ async fn is_fabric_installed( /// Get Minecraft versions supported by Forge #[tauri::command] +#[dropout_macros::api] async fn get_forge_game_versions() -> Result, String> { core::forge::fetch_supported_game_versions() .await @@ -2004,6 +2043,7 @@ async fn get_forge_game_versions() -> Result, String> { /// Get available Forge versions for a specific Minecraft version #[tauri::command] +#[dropout_macros::api] async fn get_forge_versions_for_game( game_version: String, ) -> Result, String> { @@ -2014,6 +2054,7 @@ async fn get_forge_versions_for_game( /// Install Forge for a specific Minecraft version #[tauri::command] +#[dropout_macros::api] async fn install_forge( window: Window, config_state: State<'_, core::config::ConfigState>, @@ -2109,7 +2150,9 @@ async fn install_forge( Ok(result) } -#[derive(serde::Serialize)] +#[derive(serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "core.ts")] struct GithubRelease { tag_name: String, name: String, @@ -2119,6 +2162,7 @@ struct GithubRelease { } #[tauri::command] +#[dropout_macros::api] async fn get_github_releases() -> Result, String> { let client = reqwest::Client::new(); let res = client @@ -2155,12 +2199,14 @@ async fn get_github_releases() -> Result, String> { Ok(result) } -#[derive(Serialize)] +#[derive(Serialize, TS)] +#[ts(export, export_to = "core.ts")] struct PastebinResponse { url: String, } #[tauri::command] +#[dropout_macros::api] async fn upload_to_pastebin( state: State<'_, core::config::ConfigState>, content: String, @@ -2230,6 +2276,7 @@ async fn upload_to_pastebin( } #[tauri::command] +#[dropout_macros::api] async fn assistant_check_health( assistant_state: State<'_, core::assistant::AssistantState>, config_state: State<'_, core::config::ConfigState>, @@ -2240,6 +2287,7 @@ async fn assistant_check_health( } #[tauri::command] +#[dropout_macros::api] async fn assistant_chat( assistant_state: State<'_, core::assistant::AssistantState>, config_state: State<'_, core::config::ConfigState>, @@ -2251,6 +2299,7 @@ async fn assistant_chat( } #[tauri::command] +#[dropout_macros::api] async fn list_ollama_models( assistant_state: State<'_, core::assistant::AssistantState>, endpoint: String, @@ -2260,6 +2309,7 @@ async fn list_ollama_models( } #[tauri::command] +#[dropout_macros::api] async fn list_openai_models( assistant_state: State<'_, core::assistant::AssistantState>, config_state: State<'_, core::config::ConfigState>, @@ -2273,6 +2323,7 @@ async fn list_openai_models( /// Create a new instance #[tauri::command] +#[dropout_macros::api] async fn create_instance( window: Window, state: State<'_, core::instance::InstanceState>, @@ -2284,6 +2335,7 @@ async fn create_instance( /// Delete an instance #[tauri::command] +#[dropout_macros::api] async fn delete_instance( state: State<'_, core::instance::InstanceState>, instance_id: String, @@ -2293,6 +2345,7 @@ async fn delete_instance( /// Update an instance #[tauri::command] +#[dropout_macros::api] async fn update_instance( state: State<'_, core::instance::InstanceState>, instance: core::instance::Instance, @@ -2302,6 +2355,7 @@ async fn update_instance( /// Get all instances #[tauri::command] +#[dropout_macros::api] async fn list_instances( state: State<'_, core::instance::InstanceState>, ) -> Result, String> { @@ -2310,6 +2364,7 @@ async fn list_instances( /// Get a single instance by ID #[tauri::command] +#[dropout_macros::api] async fn get_instance( state: State<'_, core::instance::InstanceState>, instance_id: String, @@ -2321,6 +2376,7 @@ async fn get_instance( /// Set the active instance #[tauri::command] +#[dropout_macros::api] async fn set_active_instance( state: State<'_, core::instance::InstanceState>, instance_id: String, @@ -2330,6 +2386,7 @@ async fn set_active_instance( /// Get the active instance #[tauri::command] +#[dropout_macros::api] async fn get_active_instance( state: State<'_, core::instance::InstanceState>, ) -> Result, String> { @@ -2338,6 +2395,7 @@ async fn get_active_instance( /// Duplicate an instance #[tauri::command] +#[dropout_macros::api] async fn duplicate_instance( window: Window, state: State<'_, core::instance::InstanceState>, @@ -2349,6 +2407,7 @@ async fn duplicate_instance( } #[tauri::command] +#[dropout_macros::api] async fn assistant_chat_stream( window: tauri::Window, assistant_state: State<'_, core::assistant::AssistantState>, @@ -2363,7 +2422,9 @@ async fn assistant_chat_stream( } /// Migrate instance caches to shared global caches -#[derive(Serialize)] +#[derive(Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "core.ts")] struct MigrationResult { moved_files: usize, hardlinks: usize, @@ -2373,6 +2434,7 @@ struct MigrationResult { } #[tauri::command] +#[dropout_macros::api] async fn migrate_shared_caches( window: Window, instance_state: State<'_, core::instance::InstanceState>, @@ -2412,7 +2474,9 @@ async fn migrate_shared_caches( } /// File information for instance file browser -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts(export, export_to = "core.ts")] struct FileInfo { name: String, path: String, @@ -2423,6 +2487,7 @@ struct FileInfo { /// List files in an instance subdirectory (mods, resourcepacks, shaderpacks, saves, screenshots) #[tauri::command] +#[dropout_macros::api] async fn list_instance_directory( instance_state: State<'_, core::instance::InstanceState>, instance_id: String, @@ -2474,6 +2539,7 @@ async fn list_instance_directory( /// Delete a file in an instance directory #[tauri::command] +#[dropout_macros::api] async fn delete_instance_file(path: String) -> Result<(), String> { let path_buf = std::path::PathBuf::from(&path); if path_buf.is_dir() { @@ -2490,6 +2556,7 @@ async fn delete_instance_file(path: String) -> Result<(), String> { /// Open instance directory in system file explorer #[tauri::command] +#[dropout_macros::api] async fn open_file_explorer(path: String) -> Result<(), String> { #[cfg(target_os = "windows")] { diff --git a/src-tauri/src/utils/api.rs b/src-tauri/src/utils/api.rs new file mode 100644 index 0000000..0d5a925 --- /dev/null +++ b/src-tauri/src/utils/api.rs @@ -0,0 +1,90 @@ +use std::collections::BTreeSet; + +#[derive(Debug)] +pub struct ApiInfo { + pub fn_name: &'static str, + pub ts_fn_name: &'static str, + pub param_names: &'static [&'static str], + pub param_defs: &'static [&'static str], + pub return_ts_promise: &'static str, + pub import_types: &'static [&'static str], + pub import_from: Option<&'static str>, +} + +inventory::collect!(ApiInfo); + +pub fn export_api_bindings(import_from: &str, export_to: &str) { + use std::collections::BTreeMap; + + let api_infos = inventory::iter::.into_iter().collect::>(); + if api_infos.is_empty() { + return; + } + + let mut ts_lines = Vec::new(); + ts_lines.push(r#"import { invoke } from "@tauri-apps/api/core""#.to_string()); + + let mut import_types: BTreeMap<&str, BTreeSet<&str>> = BTreeMap::new(); + let mut ts_funcs = Vec::new(); + for api_info in api_infos { + let api_types = api_info.import_types.iter().cloned().collect::>(); + import_types + .entry(api_info.import_from.unwrap_or(import_from)) + .or_insert_with(BTreeSet::new) + .extend(api_types.clone()); + if api_types.contains(&"Vec") { + eprintln!("???? from {}", api_info.fn_name) + } + + // Determine return generic for invoke: need the raw type (not Promise<...>) + let invoke_generic = if api_info.return_ts_promise.starts_with("Promise<") + && api_info.return_ts_promise.ends_with('>') + { + &api_info.return_ts_promise["Promise<".len()..api_info.return_ts_promise.len() - 1] + } else { + "unknown" + }; + let invoke_line = if api_info.param_names.is_empty() { + format!("invoke<{}>(\"{}\")", invoke_generic, api_info.fn_name) + } else { + format!( + "invoke<{}>(\"{}\", {{\n {}\n }})", + invoke_generic, + api_info.fn_name, + api_info.param_names.join(", ") + ) + }; + + ts_funcs.push(format!( + "export function {}({}): {} {{\n \ + return {}\n\ + }}\n", + api_info.ts_fn_name, + api_info.param_defs.join(", "), + api_info.return_ts_promise, + invoke_line + )) + } + + for (import_from, import_types) in import_types { + ts_lines.push(format!( + "import type {{ {} }} from \"{}\"", + import_types.iter().cloned().collect::>().join(", "), + import_from + )) + } + ts_lines.push("".to_string()); + ts_lines.extend(ts_funcs); + + let ts_content = ts_lines.join("\n"); + let export_to = std::path::Path::new(export_to); + if let Some(parent) = export_to.parent() { + std::fs::create_dir_all(parent).expect("Failed to create parent directory"); + } + std::fs::write(export_to, ts_content).unwrap(); +} + +#[ctor::dtor] +fn __dropout_export_api_bindings() { + export_api_bindings("@/types", "../packages/ui-new/src/client.ts"); +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index c9ac368..709439d 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -1,3 +1,5 @@ +#[cfg(test)] +pub mod api; pub mod path; pub mod zip; -- cgit v1.2.3-70-g09d2