Skip to content

Tie the desktop app's types and CSV escaping to the Rust side - #538

Merged
fstubner merged 3 commits into
mainfrom
refactor/generated-ts-types
Oct 6, 2026
Merged

fstubner merged 3 commits into
mainfrom
refactor/generated-ts-types

Conversation

@fstubner

@fstubner fstubner commented Oct 6, 2026

Copy link
Copy Markdown
Owner

Gap 4 from the audit, with a correction: I described it as "about 20 copies of the result types across the interfaces". An inventory found the CLI, TUI and MCP server already use the core structs directly through serde. The real duplicate is one hand-written TypeScript mirror of 23 core types in the desktop app (src/types/netscli.ts), plus two CSV escaping implementations (CLI --csv and the desktop export). The other export paths (CLI Markdown, TUI transcript, JSON) are different formats, not copies.

Types: checked, not replaced

  • netscli-core gets an optional ts feature that derives ts-rs bindings for the 23 result types. MacAddress, IpNet and Duration have overrides. Fields skipped when empty are marked optional, so the generated types say what the JSON really contains.
  • The generated files are committed under apps/netscli-gui/src/types/generated/. CI (Format + Clippy job) regenerates them and fails if they differ.
  • src/types/netscli.contract.ts fails tsc unless each hand-written type has exactly the generated field names and accepts every value the core can send.
  • netscli.ts stays hand-written on purpose. It is looser than the core where saved bundles from older versions lack fields, and it carries the docs the app's code relies on.
  • No drift found today. Checked that the contract catches drift: adding a field to a generated type fails tsc, and so does changing a field's type.

CSV: one set of cases for both

testdata/csv-escape.json holds 24 cases. The CLI's escape and the desktop app's csvEscape are both tested against every one. The CLI passed as it was. The desktop export failed 5:

  • It let control characters through, so an ESC sequence from a banner went into the file as-is.
  • It treated "\t1" and "\r5" as numbers, because Number() trims whitespace, and so skipped the formula guard the CLI applies.

Fixed to match the CLI (Unicode Cc, as Rust's is_control; decimal-literal number test). CHANGELOG under Fixed.

Checked locally

  • cargo clippy -D warnings with and without --features ts,mdns, cargo fmt
  • desktop: tsc, eslint, all 251 tests (18 new shared-case tests)
  • the CLI shared-case test

Types. apps/netscli-gui/src/types/netscli.ts mirrors 23 netscli-core
result types by hand and nothing checked it. netscli-core gains an
optional `ts` feature that derives ts-rs bindings for those types
(overrides for MacAddress, IpNet and Duration; fields skipped when empty
marked optional); the generated files are committed under
src/types/generated, and src/types/netscli.contract.ts fails tsc unless
each hand-written type has exactly the generated field names and accepts
every value the core can send. CI regenerates the bindings and fails if
they differ from the committed ones. netscli.ts stays hand-written: it is
deliberately looser for older saved bundles and carries the docs.
Checked: adding a field to a generated type, or changing a field's type,
fails tsc. No drift was found today.

CSV. The CLI's --csv and the desktop app's CSV export each had their own
escaping. testdata/csv-escape.json holds 24 cases both are tested against.
Against it the desktop export failed 5: it let control characters through
(an ESC sequence from a banner went into the file as-is) and treated
"\t1" and "\r5" as numbers because Number() trims whitespace, skipping the
formula guard the CLI applies. Fixed to match the CLI.
@github-actions

github-actions Bot commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Site preview: https://pr-538.netscli-site-preview.pages.dev

Built from 7530d0d with NETSCLI_PREVIEW=1 — noindex, and analytics disabled so it does not report into netscli.com's numbers.

Production is unaffected: netscli.com is served from GitHub Pages via pages.yml, which is manual-only.

@fstubner
fstubner merged commit 0b72180 into main Oct 6, 2026
17 of 19 checks passed
@fstubner
fstubner deleted the refactor/generated-ts-types branch October 6, 2026 01:35
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.

1 participant