Skip to content

[036-ts-types-from-rust]: Generate TypeScript types from Rust via ts-rs#46

Merged
archae0pteryx merged 3 commits intomainfrom
feat/036-ts-types-from-rust
May 5, 2026
Merged

[036-ts-types-from-rust]: Generate TypeScript types from Rust via ts-rs#46
archae0pteryx merged 3 commits intomainfrom
feat/036-ts-types-from-rust

Conversation

@archae0pteryx
Copy link
Copy Markdown
Contributor

@archae0pteryx archae0pteryx commented May 5, 2026

Closes #38. Implements issues/036-ts-types-from-rust.md.

Completion promise: TypeScript types for all domain structs are generated from Rust; a Rust type change causes a CI failure rather than a silent runtime drift.

Summary

  • Adds optional export-ts feature on crates/domain that derives ts_rs::TS for Focus, Task, FocusId, FocusTimer, TimerPreset, TimerStatus, Proposal, ProposalKind, NewFocus, ProposalId, Settings, Caps, Widget, Alerts, DisplayConfig, and MonitorInfo.
  • cargo test -p adhd-ranch-domain --features export-ts writes .ts files into src/types/generated/ (env in .cargo/config.toml: TS_RS_EXPORT_DIR, TS_RS_LARGE_INT=number).
  • MonitorInfo moved from src-tauri/src/ui_bridge/ to crates/domain/src/monitor.rs so it can ride the same export path.
  • Hand-written src/types/{focus,timer,proposal,settings,monitor}.ts now re-export from generated/. timer.ts keeps the PRESET_OPTIONS runtime constant.
  • Taskfile.yaml gains gen-types and gen-types:check. The latter is wired into task check and fails when committed src/types/generated/*.ts drifts from Rust.

Notes

  • ts-rs pinned to 12 not 10. v10/11 lack TS_RS_LARGE_INT, so u64/i64 would have leaked as bigint and broken existing call sites.
  • Generated Focus now requires created_at. Test fixtures across src/**/*.test.{ts,tsx} and src/api/tauriFocusReader.ts were updated to populate the field.
  • src/types/generated/ is committed (gitignoring it would defeat the diff-check). biome.json ignores the directory for formatting.

Test plan

  • task check (lint + typecheck + tests + ts-rs drift check) green locally.
  • cargo test -p adhd-ranch-domain --features export-ts regenerates the same files (no diff).
  • npm run typecheck clean.
  • All 71 frontend tests + 84 domain tests + storage/commands/http-api tests pass.

Acceptance criteria

  • ts-rs added as optional dep to crates/domain.
  • All domain structs/enums annotated with #[ts] behind export-ts feature.
  • cargo test -p adhd-ranch-domain --features export-ts generates correct .ts files.
  • Hand-written src/types/focus.ts, proposal.ts, timer.ts replaced by generated equivalents.
  • src/types/settings.ts and src/types/monitor.ts (added in 032) also generated.
  • CI diff-check fails when Rust and TS are out of sync (task gen-types:check).
  • task check green.

Summary by CodeRabbit

  • Refactor

    • Types are now generated from Rust domain models to keep frontend/backend types in sync.
    • Monitor items include display labels for clearer identification.
    • Focus records now include creation timestamps.
  • Chores

    • Build configuration updated to support automated type generation.
  • Tests / CI

    • Added verification to ensure generated types stay in sync (type-generation drift check).

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 16dc4b4f-d32f-4962-ae6f-af2fb8016683

📥 Commits

Reviewing files that changed from the base of the PR and between 0bf85f7 and 1fbf384.

📒 Files selected for processing (1)
  • Taskfile.yaml

📝 Walkthrough

Walkthrough

This PR wires ts-rs-based TypeScript generation: it adds Cargo env/config and an optional ts-rs feature, annotates domain types for conditional export, introduces a domain MonitorInfo, replaces handwritten frontend types with generated re-exports, adds Taskfile gen/check targets (and integrates them into the PR gate), and updates tests/fixtures to the new type shape (adds created_at).

Changes

TypeScript Generation from Rust

Layer / File(s) Summary
Build configuration
.cargo/config.toml, crates/domain/Cargo.toml
Adds Cargo env vars TS_RS_EXPORT_DIR = "src/types/generated" (relative) and TS_RS_LARGE_INT = "number"; adds optional ts-rs dependency and export-ts feature in crates/domain/Cargo.toml.
Domain annotations / new type
crates/domain/src/{focus.rs,proposal.rs,settings.rs,timer.rs,monitor.rs,lib.rs}
Domain types gain #[cfg_attr(feature = "export-ts", derive(ts_rs::TS))] and #[cfg_attr(feature = "export-ts", ts(export))] where appropriate; ProposalId/NewFocus add Deserialize; new MonitorInfo struct added and re-exported from lib.rs.
Generation task & CI gating
Taskfile.yaml, biome.json, issues/README.md
Adds gen-types and gen-types:check tasks; check now runs gen-types:check; src/types/generated added to Biome ignore; priority queue note updated.
UI wiring
src-tauri/src/ui_bridge/mod.rs
Removes local MonitorInfo and uses adhd_ranch_domain::MonitorInfo when constructing monitor values.
Frontend types switched to generated
src/types/{focus.ts,monitor.ts,proposal.ts,settings.ts,timer.ts}
Removes hand-written interfaces and re-exports types from ./generated/*. TimerPresetVariant redefined as Exclude<TimerPreset, { Custom: number }>; FocusId and other generated exports added.
API mapping & tests updated
src/api/tauriFocusReader.ts, src/api/fixtureFocusReader.test.ts, src/components/*.test.tsx, src/hooks/*.test.tsx, src/lib/capState.test.ts
tauriFocusReader.fromRust() now maps created_at; test fixtures updated to include created_at (and some tasks adjustments) across many tests.

Sequence Diagram

sequenceDiagram
  participant Dev as Developer
  participant Cargo as Cargo / Rust test
  participant Domain as crates/domain (ts-rs)
  participant FS as src/types/generated (filesystem)
  participant CI as CI / Taskfile

  Dev->>Cargo: run `cargo test -p adhd-ranch-domain --features export-ts`
  Cargo->>Domain: enable `export-ts` feature, execute ts-rs export tests
  Domain->>FS: write generated `.ts` files to `src/types/generated` (TS_RS_EXPORT_DIR)
  Dev->>CI: push branch
  CI->>CI: run `task gen-types:check`
  CI->>FS: verify `git diff --exit-code -- src/types/generated` and no untracked files
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped from Rust to TypeScript land,

Where ts-rs lends a helpful hand,
No more copying, no drift to chase,
Generated types fall into place—
A bunny's cheer for cleaner strands!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title '[036-ts-types-from-rust]: Generate TypeScript types from Rust via ts-rs' clearly and specifically summarizes the main change: generating TypeScript types from Rust code using the ts-rs library.
Linked Issues check ✅ Passed All objectives from issue #38 are met: ts-rs added as optional dependency [#38], domain types annotated with #[ts] attributes [#38], cargo test with export-ts feature generates .ts files [#38], hand-written types replaced with generated equivalents [#38], gen-types tasks added to CI [#38], and task check passes [#38].
Out of Scope Changes check ✅ Passed All code changes are directly related to generating TypeScript types from Rust. Updates to test fixtures reflect the addition of required created_at field needed by the new generated types; changes to MonitorInfo are part of moving it to domain as specified in #38 objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/036-ts-types-from-rust

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.


Comment @coderabbitai help to get the list of available commands and usage tips.

Adds an optional `export-ts` feature on `crates/domain` that derives
`ts_rs::TS` for every shared domain type. `cargo test -p adhd-ranch-domain
--features export-ts` writes `.ts` files into `src/types/generated/`,
driven by `TS_RS_EXPORT_DIR` and `TS_RS_LARGE_INT=number` configured
in `.cargo/config.toml`.

The hand-written `src/types/{focus,timer,proposal,settings,monitor}.ts`
now re-export from `generated/`. `src/types/timer.ts` retains the
`PRESET_OPTIONS` runtime constant alongside the type re-exports.

`MonitorInfo` moved from `src-tauri/src/ui_bridge/` into
`crates/domain/src/monitor.rs` so it can be annotated with the rest.

Taskfile gains `gen-types` and `gen-types:check`; the latter is wired
into `task check` to fail when committed `src/types/generated/*.ts`
drifts from Rust.

ts-rs is pinned to `12` rather than the `10` the issue mentioned —
v10/11 lack `TS_RS_LARGE_INT`, so `u64`/`i64` would have leaked into
TS as `bigint` and broken existing call sites that pass `number`.

Closes #38.
@archae0pteryx archae0pteryx force-pushed the feat/036-ts-types-from-rust branch from 99966af to 0bf85f7 Compare May 5, 2026 17:55
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src-tauri/src/ui_bridge/mod.rs (1)

256-264: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Populate all required MonitorInfo fields in get_monitors.

At Line 261, the adhd_ranch_domain::MonitorInfo literal only sets idx and label, but the domain struct includes additional fields (primary, position, size). This is a correctness blocker (compile failure or incomplete payload).

Proposed fix
 pub fn get_monitors(app: AppHandle<Wry>) -> Vec<MonitorInfo> {
     app.try_state::<crate::app::MonitorsState>()
         .map(|s| {
             s.0.iter()
                 .enumerate()
                 .map(|(i, m)| MonitorInfo {
                     idx: i,
                     label: m.label.clone(),
+                    primary: m.primary,
+                    position: m.position,
+                    size: m.size,
                 })
                 .collect()
         })
         .unwrap_or_default()
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/ui_bridge/mod.rs` around lines 256 - 264, The MonitorInfo
literal returned by get_monitors only sets idx and label; update the map closure
inside get_monitors (the code using app.try_state::<crate::app::MonitorsState>()
and iterating s.0) to populate the remaining fields on
adhd_ranch_domain::MonitorInfo—set primary from m.primary, position from
m.position (or construct the expected position type from m.x/m.y if needed), and
size from m.size (or m.width/m.height) so all required fields (primary,
position, size) are filled and types are converted/cloned as required.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Taskfile.yaml`:
- Around line 35-40: The drift check currently runs "git diff --exit-code --
src/types/generated" which ignores untracked files and lets newly generated .ts
files slip through; update the gen-types:check task to also detect untracked
files under src/types/generated (e.g., by running git ls-files --others
--exclude-standard -- src/types/generated and failing if it returns any paths,
or by using git status --porcelain and failing on lines starting with "?") so
the task fails when new generated files are present; change the command sequence
in the gen-types:check task to include this additional check after the gen-types
step.

---

Outside diff comments:
In `@src-tauri/src/ui_bridge/mod.rs`:
- Around line 256-264: The MonitorInfo literal returned by get_monitors only
sets idx and label; update the map closure inside get_monitors (the code using
app.try_state::<crate::app::MonitorsState>() and iterating s.0) to populate the
remaining fields on adhd_ranch_domain::MonitorInfo—set primary from m.primary,
position from m.position (or construct the expected position type from m.x/m.y
if needed), and size from m.size (or m.width/m.height) so all required fields
(primary, position, size) are filled and types are converted/cloned as required.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3f6289f6-4ea1-4aba-9149-4d4a07b6773d

📥 Commits

Reviewing files that changed from the base of the PR and between 9a620af and 0bf85f7.

⛔ Files ignored due to path filters (17)
  • Cargo.lock is excluded by !**/*.lock
  • src/types/generated/Alerts.ts is excluded by !**/generated/**
  • src/types/generated/Caps.ts is excluded by !**/generated/**
  • src/types/generated/DisplayConfig.ts is excluded by !**/generated/**
  • src/types/generated/Focus.ts is excluded by !**/generated/**
  • src/types/generated/FocusId.ts is excluded by !**/generated/**
  • src/types/generated/FocusTimer.ts is excluded by !**/generated/**
  • src/types/generated/MonitorInfo.ts is excluded by !**/generated/**
  • src/types/generated/NewFocus.ts is excluded by !**/generated/**
  • src/types/generated/Proposal.ts is excluded by !**/generated/**
  • src/types/generated/ProposalId.ts is excluded by !**/generated/**
  • src/types/generated/ProposalKind.ts is excluded by !**/generated/**
  • src/types/generated/Settings.ts is excluded by !**/generated/**
  • src/types/generated/Task.ts is excluded by !**/generated/**
  • src/types/generated/TimerPreset.ts is excluded by !**/generated/**
  • src/types/generated/TimerStatus.ts is excluded by !**/generated/**
  • src/types/generated/Widget.ts is excluded by !**/generated/**
📒 Files selected for processing (29)
  • .cargo/config.toml
  • Taskfile.yaml
  • biome.json
  • crates/domain/Cargo.toml
  • crates/domain/src/focus.rs
  • crates/domain/src/lib.rs
  • crates/domain/src/monitor.rs
  • crates/domain/src/proposal.rs
  • crates/domain/src/settings.rs
  • crates/domain/src/timer.rs
  • issues/README.md
  • issues/done/036-ts-types-from-rust.md
  • src-tauri/src/ui_bridge/mod.rs
  • src/api/fixtureFocusReader.test.ts
  • src/api/tauriFocusReader.ts
  • src/components/App.test.tsx
  • src/components/EditProposalModal.test.tsx
  • src/components/FocusCard.test.tsx
  • src/components/FocusList.test.tsx
  • src/components/PendingTray.test.tsx
  • src/components/PigDetail.test.tsx
  • src/hooks/useAppState.test.tsx
  • src/hooks/useFocuses.test.tsx
  • src/lib/capState.test.ts
  • src/types/focus.ts
  • src/types/monitor.ts
  • src/types/proposal.ts
  • src/types/settings.ts
  • src/types/timer.ts

Comment thread Taskfile.yaml
CodeRabbit on PR #46: `git diff --exit-code` ignores untracked files,
so a newly annotated Rust type that produces a brand-new `.ts` could
slip through CI. Add an explicit `git ls-files --others` check.
@archae0pteryx archae0pteryx merged commit fbcdd89 into main May 5, 2026
2 checks passed
@archae0pteryx archae0pteryx deleted the feat/036-ts-types-from-rust branch May 5, 2026 18:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

036 — Generate TypeScript types from Rust via ts-rs

1 participant