-
-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Problem
Manual synchronization between Rust backend types and TypeScript frontend interfaces is fragile and error-prone. The recent regression in #151 (commit e1af0cd) occurred because #[serde(rename_all = "camelCase")] was added to Rust structs but the TypeScript interfaces weren't fully updated to match.
Proposed Solution
Replace manual TypeScript interface synchronization with automated ts-rs generation to prevent serialization mismatches.
Why ts-rs?
- ✅ Works seamlessly with existing
serdeattributes (respectsrename_all = "camelCase") - ✅ Generates TypeScript via derive macro at compile time
- ✅ Minimal runtime overhead (generation happens during build)
- ✅ Generates
.d.tsor.tsfiles directly intosrc/types/directory - ✅ Handles Rust type mappings well (Option → ?, Vec → [], etc.)
Implementation Tasks
1. Add ts-rs dependency
Add to crates/gglib-gui/Cargo.toml:
[dependencies]
ts-rs = "7.1" # or latest stable2. Update Rust structs
In crates/gglib-gui/src/types.rs, add #[derive(TS)] to all API types:
use ts_rs::TS;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
#[ts(export, export_to = "../../../src/types/generated/")]
#[serde(rename_all = "camelCase")]
pub struct GuiModel {
// ... fields
}Apply to:
GuiModelStartServerRequestServerHealthStatusGuiSettingsHfToolSupportResponse- Any other types exposed via API
3. Configure generation
- Types generate to
src/types/generated/oncargo build - Check generated files into git for visibility
- Update
.gitignoreif needed (consider tracking generated files for transparency)
4. Update TypeScript imports
Replace manual interfaces in src/types/index.ts:
// Before:
export interface GgufModel { /* manual sync */ }
// After:
export { GuiModel as GgufModel } from './generated/GuiModel';
// Or extend if needed:
export interface GgufModel extends GuiModel { /* client-only fields */ }5. Add CI validation
Add check to CI pipeline:
# Verify generated types match committed files
cargo build --package gglib-gui
git diff --exit-code src/types/generated/Fails if developers forget to regenerate types after Rust changes.
6. Documentation
Update CONTRIBUTING.md or docs/ARCHITECTURE.md:
- Explain ts-rs workflow
- Document when/how types regenerate
- Note that Rust structs are source of truth
Benefits
- Compile-time type safety across Rust/TS boundary
- Automatic camelCase conversion via existing serde attributes
- Prevents regressions like Bug: GUI displays NaN/Missing data due to serialization mismatch #151 at build time
- Single source of truth for API contracts
- Zero runtime cost (generation at compile time)
Trade-offs
- Generated files add noise to git diffs (mitigated by separating to
generated/) - Build process couples frontend types to Rust compilation
- Developers must run
cargo buildbefore frontend work (document in README)
Related Issues
- Fixes root cause of Bug: GUI displays NaN/Missing data due to serialization mismatch #151 (serialization mismatch)
- Prevents similar issues with future backend changes
Alternative Considered
specta: More runtime-focused, better for tRPC-style workflows. Overkill for this project's simpler HTTP API pattern.