Compare commits

..
3 Commits
Author SHA1 Message Date
thallada 0f8493eb02 style: tidy the thin-binary split
Follow-up cleanup to the previous commit:

- rustfmt src/run.rs. The mechanical crate:: qualification pushed lines
  past 100 chars and left 'use crate::i18n::t' out of sort order; the
  original main.rs had been rustfmt-clean. Verified whitespace-only:
  the token stream is identical modulo trailing commas and that one
  import moving to its sorted position.
- Drop #![allow(dead_code)] from lib.rs. It existed because the library
  was a benches-only shim exposing modules nothing called. Now that the
  lib IS the application, it masks nothing -- cargo check is warning-free
  without it, so removing it restores real dead-code detection.
- Collapse the duplicated explanatory comments. The rationale lived in
  both lib.rs and main.rs; state it once as a lib.rs doc comment and
  leave main.rs as the three lines it should be. Full history is in
  docs/BUILDING.md.
2026-08-07 01:13:17 +00:00
thallada f8d3bb5b2b docs(build): record the thin-binary layout and its measured effect
Replace the 'known remaining inefficiency' section with the layout that
now exists, plus the measured before/after (clean release build: 2m14s
-> 1m46s wall, keydr units 61.0s -> 32.4s, peak ~2.0 GB unchanged).

Add a maintenance note: new modules go in src/lib.rs, not src/main.rs.
Re-declaring them in the binary would silently restore the double
compile this refactor removed.
2026-08-07 01:08:44 +00:00
thallada 0a74fe3143 refactor: move the TUI into the library so it compiles once
src/main.rs re-declared the entire module tree (`mod app; mod config;
...`), which made the binary a second independent crate. Every module --
and the whole rust-i18n translation table generated by `i18n!` -- was
compiled twice, and 281 unit tests were executed twice.

Move all application code to src/run.rs inside the library, exposed as
`keydr::run()`. main.rs is now a 10-line entry point.

Mechanical changes only; no logic was altered:
- src/run.rs is src/main.rs minus the crate-root preamble
- `fn main()` -> `pub fn run()`; `struct Cli` -> `pub struct Cli`
- bare `use app::...` / inline `ui::theme::Theme` references are now
  qualified with `crate::` (19 imports, 342 inline references), since
  they are no longer resolving from the crate root

Clean release build, measured with --timings:

                        before      after
  keydr (bin)            33.7s       0.3s
  keydr (lib)            26.0s      31.0s
  keydr total            61.0s      32.4s   (-28.6s)
  total unit-seconds    292.2s     264.5s
  wall clock            2m14s      1m46s    (-28s, -21%)

Test coverage is unchanged: 341 unique tests before and after (verified
by diffing `--list` output, not just counts). The apparent drop from
622 to 340 executions is the duplicate run disappearing -- 281 tests
previously ran once per crate copy.

Verified the binary still works: --help/--version intact, TUI renders
its welcome screen under tmux, and translations resolve (checked both
English and German).

Note: `cargo check --benches` fails with 3 errors, but those are
pre-existing on main (NgramStat field renames) and unrelated.
2026-08-07 01:05:23 +00:00
4 changed files with 7889 additions and 7562 deletions
+28 -24
View File
@@ -9,8 +9,9 @@ cargo build --release
``` ```
Plain cargo. No wrapper, no memory ceiling, no reduced optimisation. Plain cargo. No wrapper, no memory ceiling, no reduced optimisation.
A clean release build peaks at **1.92 GB** and takes **2m17s** on a A clean release build peaks at **~2.0 GB** and takes **1m46s** on a
4-core / 7.6 GB VM. 4-core / 7.6 GB VM; an incremental rebuild after touching one file is a
few seconds.
If that's not what you're seeing, read on. If that's not what you're seeing, read on.
@@ -87,35 +88,38 @@ Nothing there reduces the optimisation of shipped code.
--- ---
## Known remaining inefficiency ## Project layout
`src/main.rs` re-declares the whole module tree (`mod app; mod config; All application code lives in the **library** (`src/lib.rs``src/run.rs`
...`) instead of depending on the `keydr` lib target, so **every module and the module tree). `src/main.rs` is a 10-line entry point that calls
compiles twice** — once for the lib, once for the bin. The `i18n!` macro `keydr::run()`.
must therefore be invoked in both crate roots (`t!()` expands to
`crate::_rust_i18n_t`, so it must exist in each crate).
Measured with `cargo build --release --timings` on a clean tree, the two This matters for build time. `main.rs` used to re-declare the whole module
duplicated units are the slowest in the whole graph: tree (`mod app; mod config; ...`), which made the binary a *second
independent crate*: every module — and the entire rust-i18n translation
table — was compiled twice, and 281 unit tests ran twice.
Measured effect of consolidating it, clean release build:
``` ```
33.7s keydr (bin) before after
26.0s keydr (lib) keydr (bin) 33.7s 0.3s
1.4s generate_test_profiles (bin) keydr (lib) 26.0s 31.0s
------ keydr total 61.0s 32.4s (-28.6s)
61.0s of 292.2s total unit-seconds (21%) total unit-seconds 292.2s 264.5s
wall clock 2m14s 1m46s (-28s, -21%)
``` ```
So restructuring `main.rs` to `use keydr::...` would save roughly 26s of Test coverage is unchanged — 341 unique tests before and after. The
compile work — real, but nowhere near half the build. Wall-clock saving is execution count dropping from 622 to 340 is the duplicate run disappearing.
smaller still, since those units partly overlap with dependency
compilation across 3 parallel jobs (292s of unit-work compressed into
~2m15s wall). Worth doing for code hygiene; not a build-performance
emergency.
Note the remaining ~79% is dependency compilation (`reqwest` 12.7s, **Keep it this way:** if you add a module, declare it in `src/lib.rs`, not
`clap_builder` 11.4s, `toml_edit` 9.3s, `tokio` 8.0s...), which is `src/main.rs`. Re-declaring modules in the binary would silently restore
one-time work that incremental builds skip entirely. the double compile.
The remaining ~79% of build time is dependency compilation (`reqwest`
12.7s, `clap_builder` 11.4s, `toml_edit` 9.3s, `tokio` 8.0s...), which
incremental builds skip entirely.
--- ---
+9 -7
View File
@@ -1,12 +1,12 @@
// Library target exists solely for criterion benchmarks. //! keydr — terminal typing tutor with adaptive learning.
// The binary entry point is main.rs; this file re-declares the module tree so //!
// that bench harnesses can import types via `keydr::engine::*` / `keydr::session::*`. //! All application code lives here, including the TUI event loop (`run`).
// Most code is only exercised through the binary, so suppress dead_code warnings. //! `src/main.rs` is a thin wrapper so nothing is compiled twice; see
#![allow(dead_code)] //! docs/BUILDING.md. New modules belong in this file, not in main.rs.
rust_i18n::i18n!("locales", fallback = "en"); rust_i18n::i18n!("locales", fallback = "en");
// Public: used by benchmarks and the generate_test_profiles binary // Public: used by the binary, benchmarks, and generate_test_profiles.
pub mod config; pub mod config;
pub mod engine; pub mod engine;
pub mod keyboard; pub mod keyboard;
@@ -14,9 +14,11 @@ pub mod l10n;
pub mod session; pub mod session;
pub mod store; pub mod store;
// Private: required transitively by engine/session (won't compile without them)
mod app; mod app;
mod event; mod event;
mod generator; mod generator;
mod i18n; mod i18n;
mod run;
mod ui; mod ui;
pub use run::run;
+2 -7531
View File
File diff suppressed because it is too large Load Diff
+7850
View File
File diff suppressed because it is too large Load Diff