mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 14:55:43 +00:00
chore(ipc): bootstrap tauri-specta generator (1 command, no cutover)
Stand up the FE<->BE contract pipeline end to end with near-zero surface: a tauri_specta::Builder collects one proven command (greet) and exports typed TS bindings to src/generated/bindings.ts. The existing generate_handler! stays the live invoke handler — no FE change yet. Safety: the export runs only under #[cfg(debug_assertions)] (debug launch) and a headless test, never in a release build, so a specta RC break can never block a release cargo build; the committed bindings.ts is plain TypeScript for tsc. The snapshot is committed (gitignore exception) so CI diffs catch contract drift. Pinned deps: specta / tauri-specta =2.0.0-rc.25, specta-typescript =0.0.12 (the latest mutually-consistent set; rc.21 + specta-typescript 0.0.9 no longer resolve). Grow collect_commands! crate-by-crate next, starting with psysonic-library.
This commit is contained in:
+6
-2
@@ -37,8 +37,12 @@ src-tauri/lcov.info
|
|||||||
# Frontend test coverage
|
# Frontend test coverage
|
||||||
coverage/
|
coverage/
|
||||||
|
|
||||||
# Generated at build/test/dev time (scripts/generate-release-notes-bundle.mjs)
|
# Generated at build/test/dev time (scripts/generate-release-notes-bundle.mjs).
|
||||||
src/generated/
|
# Ignore the contents (not the dir) so the committed tauri-specta FE↔BE contract
|
||||||
|
# snapshot below can be re-included — a `!` exception cannot escape an ignored dir.
|
||||||
|
src/generated/*
|
||||||
|
# ...except the committed tauri-specta contract snapshot, so CI diffs catch drift.
|
||||||
|
!src/generated/bindings.ts
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
CLAUDE.md
|
CLAUDE.md
|
||||||
|
|||||||
Generated
+92
@@ -2,6 +2,12 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "Inflector"
|
||||||
|
version = "0.11.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler2"
|
name = "adler2"
|
||||||
version = "2.0.1"
|
version = "2.0.1"
|
||||||
@@ -4140,6 +4146,8 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"souvlaki",
|
"souvlaki",
|
||||||
|
"specta",
|
||||||
|
"specta-typescript",
|
||||||
"symphonia",
|
"symphonia",
|
||||||
"symphonia-adapter-libopus",
|
"symphonia-adapter-libopus",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
@@ -4154,6 +4162,7 @@ dependencies = [
|
|||||||
"tauri-plugin-store",
|
"tauri-plugin-store",
|
||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tauri-plugin-window-state",
|
"tauri-plugin-window-state",
|
||||||
|
"tauri-specta",
|
||||||
"thread-priority",
|
"thread-priority",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
@@ -5381,6 +5390,57 @@ dependencies = [
|
|||||||
"zvariant 3.15.2",
|
"zvariant 3.15.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "specta"
|
||||||
|
version = "2.0.0-rc.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38f9a30cbcbb7011f1da7d73483983bf838af123883e45f2b36ed76328df9c50"
|
||||||
|
dependencies = [
|
||||||
|
"paste",
|
||||||
|
"rustc_version",
|
||||||
|
"specta-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "specta-macros"
|
||||||
|
version = "2.0.0-rc.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ce14957ecc2897f1f848b8255b6531d13ddf49cbcf506b7c2c9fb1d005593bb"
|
||||||
|
dependencies = [
|
||||||
|
"Inflector",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "specta-serde"
|
||||||
|
version = "0.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee8a72b755ddb8949fd8f17c5db43f0e8a806ea587d9bc602ee3f73240c00029"
|
||||||
|
dependencies = [
|
||||||
|
"specta",
|
||||||
|
"specta-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "specta-typescript"
|
||||||
|
version = "0.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "639404ee95557f2f8b7e4cb773ffefd45304c7ab8ba21ac83b69051595e083c0"
|
||||||
|
dependencies = [
|
||||||
|
"specta",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "specta-util"
|
||||||
|
version = "0.0.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29b1fc02b446f7244a92924fe68c0555921209f1d342990cd1539e9138e69502"
|
||||||
|
dependencies = [
|
||||||
|
"specta",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
@@ -5821,6 +5881,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"serialize-to-javascript",
|
"serialize-to-javascript",
|
||||||
|
"specta",
|
||||||
"swift-rs",
|
"swift-rs",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-macros",
|
"tauri-macros",
|
||||||
@@ -6133,6 +6194,37 @@ dependencies = [
|
|||||||
"wry",
|
"wry",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-specta"
|
||||||
|
version = "2.0.0-rc.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee080f36d2ac17ce2f3a82fb53f02d664e8345457de51b56dad3c394dacc41a2"
|
||||||
|
dependencies = [
|
||||||
|
"heck 0.5.0",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"specta",
|
||||||
|
"specta-serde",
|
||||||
|
"specta-typescript",
|
||||||
|
"specta-util",
|
||||||
|
"tauri",
|
||||||
|
"tauri-specta-macros",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-specta-macros"
|
||||||
|
version = "2.0.0-rc.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a59dfdce06c98d8d211619bea5fdb39486d8a8c558e12b2d2ce255972320012"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"heck 0.5.0",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "2.9.2"
|
version = "2.9.2"
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ psysonic-library = { path = "crates/psysonic-library" }
|
|||||||
psysonic-syncfs = { path = "crates/psysonic-syncfs" }
|
psysonic-syncfs = { path = "crates/psysonic-syncfs" }
|
||||||
psysonic-integration = { path = "crates/psysonic-integration" }
|
psysonic-integration = { path = "crates/psysonic-integration" }
|
||||||
tauri = { version = "2", features = ["protocol-asset", "tray-icon", "image-png"] }
|
tauri = { version = "2", features = ["protocol-asset", "tray-icon", "image-png"] }
|
||||||
|
specta = "=2.0.0-rc.25"
|
||||||
|
specta-typescript = "=0.0.12"
|
||||||
|
tauri-specta = { version = "=2.0.0-rc.25", features = ["derive", "typescript"] }
|
||||||
tauri-plugin-single-instance = "2"
|
tauri-plugin-single-instance = "2"
|
||||||
tauri-plugin-shell = "2"
|
tauri-plugin-shell = "2"
|
||||||
tauri-plugin-global-shortcut = "2"
|
tauri-plugin-global-shortcut = "2"
|
||||||
|
|||||||
@@ -78,7 +78,39 @@ fn set_app_user_model_id() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// FE↔BE contract via tauri-specta. The builder collects `#[specta::specta]`
|
||||||
|
/// commands and exports typed TS bindings; the existing `generate_handler!` stays
|
||||||
|
/// the live invoke handler (no cutover yet). Export runs only in debug builds /
|
||||||
|
/// tests, so a specta RC break can never block a release `cargo build` — the
|
||||||
|
/// committed `bindings.ts` is plain TypeScript for `tsc`. Grow `collect_commands!`
|
||||||
|
/// crate-by-crate (see the specta-contract plan).
|
||||||
|
fn specta_builder() -> tauri_specta::Builder<tauri::Wry> {
|
||||||
|
tauri_specta::Builder::<tauri::Wry>::new()
|
||||||
|
.commands(tauri_specta::collect_commands![
|
||||||
|
crate::lib_commands::app_api::core::greet
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// TS exporter config. Kept as a single seam so the exporter options (header /
|
||||||
|
/// per-type BigInt-style handling for i64 DTOs) are configured in one place as
|
||||||
|
/// commands are added crate-by-crate.
|
||||||
|
fn bindings_exporter() -> specta_typescript::Typescript {
|
||||||
|
specta_typescript::Typescript::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Regenerate the committed bindings on a debug launch (matches the dev workflow;
|
||||||
|
/// the CI freshness gate runs the equivalent export in a test — see `specta_export`).
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
fn export_specta_bindings() {
|
||||||
|
specta_builder()
|
||||||
|
.export(bindings_exporter(), "../src/generated/bindings.ts")
|
||||||
|
.expect("failed to export typescript bindings");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
export_specta_bindings();
|
||||||
|
|
||||||
// Windows: bind this process to an explicit AppUserModelID before any window
|
// Windows: bind this process to an explicit AppUserModelID before any window
|
||||||
// or the SMTC media controls are created, so the OS can resolve the app
|
// or the SMTC media controls are created, so the OS can resolve the app
|
||||||
// name/icon for taskbar grouping and the media tile (#1102 follow-up: the
|
// name/icon for taskbar grouping and the media tile (#1102 follow-up: the
|
||||||
@@ -922,3 +954,15 @@ pub fn run() {
|
|||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running Psysonic");
|
.expect("error while running Psysonic");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod specta_export {
|
||||||
|
// Headless generator + freshness seed: runs the same export as a debug launch
|
||||||
|
// without a GUI, so `cargo test` regenerates `bindings.ts` and CI can diff it.
|
||||||
|
#[test]
|
||||||
|
fn bindings_are_exportable() {
|
||||||
|
super::specta_builder()
|
||||||
|
.export(super::bindings_exporter(), "../src/generated/bindings.ts")
|
||||||
|
.expect("failed to export typescript bindings");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use crate::runtime_subsonic_wire_user_agent;
|
|||||||
use crate::analysis_cache;
|
use crate::analysis_cache;
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
#[specta::specta]
|
||||||
pub(crate) fn greet(name: &str) -> String {
|
pub(crate) fn greet(name: &str) -> String {
|
||||||
format!("Hello, {}!", name)
|
format!("Hello, {}!", name)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
mod backup;
|
mod backup;
|
||||||
mod cli_bridge;
|
mod cli_bridge;
|
||||||
mod core;
|
// `pub(crate)` so tauri-specta's `collect_commands!` can reach the `#[specta::specta]`
|
||||||
|
// helper macro by full path (a `pub use` of the fn does not carry the macro).
|
||||||
|
pub(crate) mod core;
|
||||||
mod integration;
|
mod integration;
|
||||||
mod migration;
|
mod migration;
|
||||||
mod network;
|
mod network;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// This file has been generated by Tauri Specta. Do not edit this file manually.
|
||||||
|
|
||||||
|
import { invoke as __TAURI_INVOKE } from "@tauri-apps/api/core";
|
||||||
|
|
||||||
|
/** Commands */
|
||||||
|
export const commands = {
|
||||||
|
greet: (name: string) => __TAURI_INVOKE<string>("greet", { name }),
|
||||||
|
};
|
||||||
|
|
||||||
Reference in New Issue
Block a user