Skip to content

refactor: split the four most-edited modules into directories - #57

Merged
MotherSphere merged 3 commits into
mainfrom
refactor/split-modules
Jul 25, 2026
Merged

refactor: split the four most-edited modules into directories#57
MotherSphere merged 3 commits into
mainfrom
refactor/split-modules

Conversation

@MotherSphere

Copy link
Copy Markdown
Member

Organizes src/ by splitting the files that actually hurt, chosen from data rather than from size alone.

Why these four

Size is the wrong metric on its own. Measured over six months:

File Lines Commits Action
ui/theme.rs 2935 4 left alone
update.rs 1836 24 split
ui/settings.rs 1707 7 left alone
i18n.rs 1523 12 split
github.rs 1432 15 split
main.rs 606 20 split

ui/theme.rs is the biggest file in the project and the least touched, because it is a palette table read by lookup rather than top to bottom; splitting it would be churn for nothing. main.rs is small but the second most-edited file, because it doubled as crate root and App shell. settings.rs is one long view function that nobody fights with.

What moved

  • update.rsupdate/ (the one that pays). All 70 match arms lived in one function. mod.rs now holds the dispatch table alone, one line per message, so the whole protocol is visible at a glance, and the bodies sit next to their domain: store.rs (install/update/uninstall/release notes/favorites), github_auth.rs (device flow + catalog fetch), launcher.rs (self-update), preferences.rs (settings + toggles), keyboard.rs (global key handling, 141 lines of it), onboarding.rs (welcome + tutorial). 1836 lines → 820 + six focused files.
  • main.rsmain.rs + app.rs. The crate root is now 46 lines: the crate attribute, the module list, main(). boot/view/subscription/theme moved to app.rs and became pub(crate) since main() now calls them across a module boundary.
  • i18n.rsi18n/{mod,fr,en}.rs. Both locale tables shared one file, so adding a string produced a diff straddling two languages ~350 lines apart.
  • github.rsgithub/{mod,http,types,catalog,releases}.rs, split by layer. mod.rs re-exports everything, so no call site outside the module changed: the rest of the crate still names crate::github::X whatever file X now lives in.

Behaviour is unchanged, and here is why that is checkable

These are pure moves. Two specific hazards, both addressed:

  • return inside a moved match arm. An arm body became a method returning Task<Message>, so return X yields the same value from update() as before. Verified across all extracted methods.
  • Arm ordering. Every arm matches a distinct variant, so order was never significant. rustc's exhaustiveness check plus a clean clippy --all-targets -D warnings (which would flag an unreachable pattern) prove the table still covers all 70 variants with no redundant arm.

Visibility grew only where the split demands it: github_token and dispatch_next_queued_update to pub(super), and in github/ a few helpers that were private to the old single file (cached_get, GITHUB_API, build_client, the three wire structs) to pub(crate).

Verification

  • 119 tests pass, clippy --all-targets -D warnings clean, fmt clean, cross-compiled for Windows with --all-targets.
  • Real cargo build of the binary, not just check.
  • The i18n key-parity test was confirmed to still guard by breaking parity on purpose and watching fr_and_en_have_identical_key_sets fail, then restoring.

Blame

This is one squashed pure-move commit, so git blame will point here for ~2000 moved lines. A follow-up adds .git-blame-ignore-revs with this commit's SHA (which cannot be known before the squash), so blame keeps pointing at whatever change actually wrote each line.

Left deliberately

ui/theme.rs and ui/settings.rs, per the table above. Grouping the remaining root files (download.rs, scan.rs, persistence.rs, signing.rs, oauth.rs…) into domain directories is possible but is cosmetic churn: they are all under 700 lines and none is a hotspot.

update.rs was the most-edited file in the project (24 commits in six months)
and held all 70 match arms in one 1836-line function, so every feature landed
in the same place and every diff read the same.

mod.rs now keeps the dispatch table alone - one line per message, the whole
protocol visible at a glance - and the bodies live next to their domain:

  store.rs        install, update, uninstall, release notes, favorites
  github_auth.rs  device-flow login and catalog fetching
  launcher.rs     self-update: check, download, apply, relaunch
  preferences.rs  settings panel and every user toggle
  keyboard.rs     global key handling
  onboarding.rs   first-launch welcome and tutorial

Pure move: no behaviour change. Arm bodies became methods returning
Task<Message>, so a `return` inside one still yields the same value from
update(); the arms are distinct variants, so order was never significant.
rustc's exhaustiveness check plus a clean clippy -D warnings prove the table
still covers all 70 variants with no redundant arm.
main.rs was the second most-edited file (20 commits in six months) because it
doubled as the crate root and the App shell: boot, view, subscription and theme
all lived there, so every feature touching startup or the root layout landed in
the entry point.

main.rs is now 47 lines - the crate attribute, the module list and main(). The
shell moved to app.rs unchanged; boot/title/view/subscription/theme became
pub(crate) since main() now calls them across a module boundary, and
ui/github_panel.rs now names crate::state::GitHubState directly instead of
relying on a private import that happened to sit at the crate root.
i18n.rs held both locale tables in one 1523-line file, so adding a single
string produced a diff straddling two languages ~350 lines apart. One file per
locale now: fr.rs, en.rs, and a mod.rs that keeps the Locale type and the
lookup. The key-set parity test still guards the pair - verified by breaking
parity on purpose and watching it fail.

github.rs mixed three concerns at 1432 lines: the HTTP client and its
conditional-request cache, the wire types, and the catalog and release logic
built on top. Split by layer into http/types/catalog/releases, with mod.rs
re-exporting everything so no call site outside the module changed - the rest of
the crate still names crate::github::X whatever file X now lives in. A handful of
helpers that were private to the old single file (cached_get, GITHUB_API,
build_client, the GithubRepo/Content/Readme wire structs) are now pub(crate),
which is the visibility the split requires and nothing wider.
@MotherSphere
MotherSphere merged commit 430f238 into main Jul 25, 2026
3 checks passed
@MotherSphere
MotherSphere deleted the refactor/split-modules branch July 25, 2026 11:41
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