storageshower — Engineering Report
Description: storageshower — Engineering report. Cyberpunk disk usage TUI in Rust: 13,625 src lines + 49,922 integration test lines, 30 builtin themes, 3,692 test functions, ratatui + crossterm, macOS IOKit + Linux /proc/diskstats live I/O, SMART probes, NFS latency, drill-down explorer, theme editor.

## >_EXECUTIVE SUMMARY

storageshower is a single-binary TUI for disk usage monitoring written in Rust on top of ratatui + crossterm. It paints per-mount usage bars in four styles across 30 builtin cyberpunk palettes, refreshes telemetry (load avg, memory, CPU, swap, processes, uptime, network, battery) on a background thread behind an `Arc<Mutex<>>`, probes per-device SMART status and per-mount disk I/O throughput against IOKit on macOS and `/proc/diskstats` on Linux, samples network-filesystem latency with a timed `read_dir`, drills into mounts with a recursive directory explorer, and ships a live per-channel theme editor that round-trips to a TOML config. 13,625 Rust source lines + 49,922 integration test lines across 1,336 test files; 3,692 test functions total; 8 production + 4 dev direct dependencies (269 in the resolved tree); 245 commits since 2026-03-26.

13,625
Rust Src Lines
49,922
Test Src Lines
30
Builtin Themes
3,692
Test Functions
1,336
Test Files
13
Source Files
8
Direct Deps
269
Resolved Crates
245
Git Commits
v0.28.8
Current Release

Source distribution — 63,891 Rust lines

49,922 tests / 13,625 src / 344 bench · 78.1% tests

Tests: 1,336 files in `tests/`. Src: 13 modules in `src/`. Bench: 1 file (`benches/bench.rs`, 344 lines, criterion-driven). Test:src ratio is 3.66:1.

## #SOURCE LAYOUT

Thirteen modules under `src/`. Largest: `ui.rs` (rendering) and `keys.rs` (keyboard dispatch). `system.rs` hosts every platform probe (IOKit / `/proc/diskstats` / SMART / network FS latency / sysinfo). `app.rs` owns the `App` state machine and refresh loop.

Module | Total lines | Prod lines | Inline test lines | Role
--- | --- | --- | --- | ---
`src/ui.rs` | 3,864 | 3,449 | 415 | ratatui widgets: disk list, drill-down, header / footer / tooltips, theme chooser, theme editor, popup chrome, palette tables
`src/keys.rs` | 1,932 | 687 | 1,245 | Keyboard event dispatch: navigation, sort, filter, drill-down, theme editor, threshold cycles, bookmarks, export
`src/cli.rs` | 1,534 | 455 | 1,079 | `clap` derive: every flag, `--no-flag` inverses, `--list-colors`, `--export-theme`, config merge
`src/system.rs` | 1,511 | 778 | 733 | Platform probes: IOKit byte counters (macOS), `/proc/diskstats` (Linux), SMART, NFS latency, sysinfo wrappers, mount discovery
`src/app.rs` | 1,325 | 462 | 863 | `App` state, refresh thread, alert tracking, bookmark set, filter buffer, view mode (Disks ↔ DrillDown)
`src/mouse.rs` | 923 | 235 | 688 | Mouse event router: hit-testing rows / headers / column separators / popup / theme rows, drag-to-resize, scroll-wheel
`src/prefs.rs` | 685 | 108 | 577 | TOML config schema, load / save, custom-theme merge, default `storageshower.default.conf` generator
`src/helpers.rs` | 683 | 69 | 614 | Pure helpers: byte-pretty-printing (`human` / `gib` / `mib` / `bytes`), mount-name compaction, gradient interpolation
`src/types.rs` | 679 | 263 | 416 | `ColorMode` (30 variants), `ThemeColors`, `UnitMode`, `SmartHealth`, `DiskEntry`, `DirEntry`, `SysStats`, `HoverZone`, `ViewMode`
`src/columns.rs` | 234 | 49 | 185 | Column layout: mount / used / size / pct / bar widths, drag-resize math
`src/testutil.rs` | 135 | 92 | 43 | Shared test fixtures: synthetic `DiskEntry` / `App` builders, temp-config helpers
`src/main.rs` | 106 | 106 | 0 | Entry point: terminal setup / teardown, `App` bootstrap, event loop, signal handling
`src/lib.rs` | 14 | 10 | 4 | Module declarations + crate-level lint allows; binary & integration tests both consume the library
Total | 13,625 | 6,763 | 6,862 | Inline tests ≈ production code (50/50 split inside `src/`); integration tests (49,922 lines) sit on top

Inline-test rows count lines from each module's `#[cfg(test)] mod tests` block to EOF. `main.rs` and `lib.rs` have no inline tests by design; all behavioral coverage of the binary lives in `tests/`.

## *TEST SURFACE

1,336 integration test files in `tests/` plus inline `#[cfg(test)]` modules in every source file. 3,692 total `#[test]` functions. Integration tests dominate (3,101 of 3,692) and target whole-binary behavior: CLI parsing, config merge, scan output, drill-down sorting, threshold transitions, palette equivalence.

3,692
Test Functions
3,101
Integration
591
Inline
1,336
Test Files
49,922
Test LOC
3.66:1
Test : Src

Inline coverage by module (counted as `#[test]` attributes inside each `#[cfg(test)] mod tests`):

Module | Inline #[test] fns
--- | ---
`src/keys.rs` | 120
`src/cli.rs` | 109
`src/helpers.rs` | 98
`src/app.rs` | 57
`src/system.rs` | 45
`src/ui.rs` | 41
`src/types.rs` | 40
`src/mouse.rs` | 27
`src/prefs.rs` | 26
`src/columns.rs` | 23
`src/testutil.rs` | 5
`src/main.rs` | 0
Total inline | 591

Integration test files follow a verbose naming convention — each file documents the scenario in its filename so failing tests are self-describing in CI output:

    tests/cli_apply_header_no_bars_no_local_integration.rs
    tests/cli_binary_void_walker_deep_net_integration.rs
    tests/helpers_format_bytes_72_kib_human_integration.rs
    tests/scan_directory_two_hundred_sixty_nine_files_integration.rs
    … (1,332 more)

Test fixtures live in `src/testutil.rs` behind `pub(crate)` and are exposed only when `#[cfg(test)]`. End-to-end CLI tests spawn the compiled binary under `tempfile`-managed config dirs and assert against stdout / stderr / exit code rather than poking internal state.

## @SUBSYSTEM BREAKDOWN

Eleven feature subsystems sit between the event loop and the renderer. Every probe is cancellable and bounded so the UI thread never blocks — slow filesystems return `None` after the deadline and rerun next tick.

#### Render engine

Per-mount usage bar in four glyph styles. Cycles via `b`. Theme palette consumed as `(blue, green, purple, light_purple, royal, dark_purple)` from `ui::palette()`; mapped to ratatui `Color`s per channel.

- Gradient: `████▓▓▒▒░░` with interpolated color along the fill
- Solid / thin / ascii fall back for low-color terminals or muscle-memory aesthetics

#### Telemetry core

Background thread refreshes `SysStats` every 3 s through an `Arc<Mutex<SysStats>>`. Captured fields: hostname, load_avg (1/5/15), mem used/total, swap used/total, CPU count, process count, kernel, arch, uptime, OS name + version. UI lock-and-copy on each frame; never blocks render.

#### Alert subsystem

Tracks `(mount, was_above_threshold)` per disk between ticks. Transitions into warn / crit fire: terminal bell, 2 s pulsing red border flash, dark-red row highlight, status-bar message. Transitions out silently clear. Thresholds default 70 % / 90 %, cycle via `t` / `z`.

#### Drill-down explorer

`Enter` on a mount swaps `ViewMode::Disks` → `ViewMode::DrillDown`. Recursive size scan runs on a worker thread; entries paint as they're computed with a progress bar. Sort by size or name, gradient bars relative to the largest entry, breadcrumb up via `Backspace`.

#### Live disk I/O

Per-tick delta of byte counters → `(read_rate, write_rate)` overlaid on each row as `▲1.2M/s ▼500K/s`. macOS: IOKit `IOBlockStorageDriver` via `IOServiceMatching`, mounts mapped via `getmntinfo`. Linux: `/proc/diskstats` sector counters (× 512 bytes), mapped via `/proc/mounts`.

#### SMART health

Per-base-device SMART probe cached and propagated to every mount sharing the device. macOS: parse `diskutil info <dev>` `SMART Status:` line. Linux: read `/sys/block/<dev>/device/state`. Green `✔` verified, red `✗` failing, neutral for unknown.

#### Network FS latency

Detects mount types: `nfs`, `nfs4`, `cifs`, `smbfs`, `afp`, `ncp`, `fuse.sshfs`, `fuse.rclone`, `fuse.s3fs`, `9p`, `afs`. Probes with a 2 s deadlined `read_dir`. Color-coded badge: green < 50 ms, yellow 50–200 ms, red > 200 ms or timeout. Cached per mount so the probe never re-runs more than once per refresh interval.

#### Filter mode

`/` enters case-insensitive substring filter. Buffer supports `Ctrl+a`/`e`/`b`/`f`/`w`/`u`/`k`/`h`, `Backspace`, `Delete`, `Home`/`End`, arrow keys. `Enter` commits, `Esc` cancels, `0` clears.

#### Theme chooser & editor

`c` pops a chooser with mouse + scroll preview; clicking outside reverts. `C` opens a per-channel editor with `j`/`k` to select channel and `h`/`l` (±1) or `H`/`L` (±10) to adjust. `Enter` saves to `~/.storageshower.conf` after a name prompt.

#### Mouse router

Hit-tests every visible region per event: disk rows, column headers, column separators (drag), title segments, footer segments, popup rows, theme grid. Right-click pops a verbose tooltip (capacity, rank, headroom, SMART, I/O). Scroll wheel walks selection in the active view.

#### Bookmark / export

`B` toggles bookmark on the selected mount (persisted in the `bookmarks` config array, sorted ahead of everything else). `e`/`E` exports the current disk matrix (mount, percent, used, total) to `~/.storageshower.export.txt`. `o`/`O` opens the mount in the system file manager. `y`/`Y` copies the path.

## %THEME SYSTEM

30 builtin palettes defined as `ColorMode` variants in `src/types.rs`. Each maps to a six-channel `ThemeColors` (`blue`, `green`, `purple`, `light_purple`, `royal`, `dark_purple`) consumed by `ui::palette()` and `ui::palette_for_prefs()`. User themes ship as `themes/*.toml` and merge into the user's `~/.storageshower.conf` as `[custom_themes.<name>]` blocks, activated via `active_theme` or `--theme`.

CLI key | Display name | CLI key | Display name
--- | --- | --- | ---
`default` | Neon Sprawl | `cyberfrost` | Cyber Frost
`green` | Acid Rain | `plasmacore` | Plasma Core
`blue` | Ice Breaker | `steelnerve` | Steel Nerve
`purple` | Synth Wave | `darksignal` | Dark Signal
`amber` | Rust Belt | `glitchpop` | Glitch Pop
`cyan` | Ghost Wire | `holoshift` | Holo Shift
`red` | Red Sector | `nightcity` | Night City
`sakura` | Sakura Den | `deepnet` | Deep Net
`matrix` | Data Stream | `lasergrid` | Laser Grid
`sunset` | Solar Flare | `quantumflux` | Quantum Flux
`neonnoir` | Neon Noir | `biohazard` | Bio Hazard
`chromeheart` | Chrome Heart | `darkwave` | Darkwave
`bladerunner` | Blade Runner | `overlock` | Overlock
`voidwalker` | Void Walker | `megacorp` | Megacorp
`toxicwaste` | Toxic Waste | `zaibatsu` | Zaibatsu

The `color_mode_next_cycles_full_palette_exactly` test in `src/types.rs` pins the cycle order. Any new variant added to `ColorMode::ALL` bumps that test — an immune system against silently dropping a palette.

## !PLATFORM PROBES

Every OS-touching code path lives in `src/system.rs`. The probes are `cfg(target_os = "macos")` / `cfg(target_os = "linux")` gated and return uniform types so the rest of the codebase (renderer, alert subsystem, drill-down) is platform-agnostic.

#### Mount enumeration

macOS: `getmntinfo` (BSD) yields `struct statfs` → mount path + device.
Linux: parse `/proc/mounts`.
Common: `sysinfo::Disks` for usage figures (used / total / fs name / kind).

#### Disk I/O byte counters

macOS: IOKit `IOServiceMatching("IOBlockStorageDriver")` → `Statistics` dict → `Bytes (Read)` / `Bytes (Write)`. Delta vs last tick ÷ elapsed = rate.
Linux: fields 6 + 10 from `/proc/diskstats` (sectors read / written) × 512 bytes.

#### SMART status

macOS: shell out to `diskutil info /dev/disk0`, regex `SMART Status:` line, map to `Verified` / `Failing`.
Linux: read `/sys/block/<dev>/device/state`, treat `"running"` as verified, everything else as unknown / failing.

#### Network FS latency

Detect mount type from the filesystem string (`nfs`, `nfs4`, `cifs`, `smbfs`, `afp`, `ncp`, `fuse.sshfs`, `fuse.rclone`, `fuse.s3fs`, `9p`, `afs`). Spawn a worker thread, `read_dir` the mount, time to first entry. 2 s deadline; on timeout, return `None` — UI renders no badge.

#### Sysinfo wrapper

`sysinfo` crate refreshed on the 3-second background tick. Fields exposed via `SysStats`: hostname, load_avg, mem used/total, swap used/total, CPU count, process count, kernel, arch, uptime, OS name + version. Network IP, battery, TTY pulled from `libc` / IOKit / Linux `/sys` outside of sysinfo.

#### Local disk detection

HDD / SSD vs network / virtual decided from the `DiskKind` + filesystem type. `--local-only` filters out anything that isn't a physical disk; `--no-virtual` drops tmpfs / devfs / proc / sysfs etc.

## +DEPENDENCIES

Eight direct production dependencies + four dev-dependencies. `Cargo.lock` resolves to 269 crates total — the bulk are transitive pulls from ratatui (signal handling, terminal backends) and sysinfo (per-OS shims).

Crate | Version | Role
--- | --- | ---
`ratatui` | 0.30 | TUI framework: widgets, layout, terminal backend abstraction
`crossterm` | 0.29 | Cross-platform terminal control: events, raw mode, mouse, alt screen
`sysinfo` | 0.38 | OS-agnostic disk / mem / CPU / proc / load_avg / hostname / uptime
`clap` | 4 (derive) | CLI parsing with `derive` macros, value-enum for `UnitMode`
`serde` | 1 (derive) | Config + theme schema serialization (`ThemeColors`, `UnitMode`, etc.)
`toml` | 1.1 | Config file format: parse `~/.storageshower.conf`, emit `--export-theme` output
`dirs` | 6 | Cross-platform home directory lookup for the default config path
`libc` | 0.2 | Unix syscalls: `getmntinfo`, `statfs`, time, TTY detection, `getifaddrs`
— dev-dependencies —
`criterion` | 0.8 (html_reports) | Bench framework: `benches/bench.rs` measures bar rendering, scan, palette resolve
`serde_json` | 1 | JSON round-trip tests for `ThemeColors` and config snapshots
`tempfile` | 3 | Per-test isolated config dirs for integration tests
`toml` | 1.1 | Dev-side roundtrip checks of the on-disk config format

Release profile: `opt-level = 3`, `lto = true`, `strip = true`. Rust 2024 edition; MSRV ≥ 1.85. CI matrix targets `x86_64-apple-darwin` (macos-latest), `aarch64-apple-darwin`, `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu`.

## ;PUBLIC API SURFACE

`src/lib.rs` exposes the library so integration tests and the binary share a single compilation unit. Eight `pub mod`s; `keys` and `mouse` stay private; `testutil` is `pub(crate)` behind `#[cfg(test)]`.

Module | Visibility | Surface
--- | --- | ---
`app` | pub | `App` state, refresh loop, view-mode transitions, alert tracking
`cli` | pub | `clap::Parser`-derived `Args`, config merge, `--list-colors` / `--export-theme` handlers
`columns` | pub | Column layout struct + resize math (mount / used / size / pct / bar widths)
`helpers` | pub | Byte formatters, mount-name compaction, gradient interpolation, color helpers
`prefs` | pub | `Prefs` TOML schema, load / save, custom-theme merge
`system` | pub | Mount enumeration, disk I/O byte counters, SMART, NFS latency, sysinfo wrapper
`types` | pub | `ColorMode`, `ThemeColors`, `UnitMode`, `SmartHealth`, `DiskEntry`, `DirEntry`, `SysStats`, `HoverZone`, `ViewMode`
`ui` | pub | `palette()`, `palette_for_prefs()`, every render fn for ratatui widgets
`keys` | private | Keyboard event dispatch — consumed only by the binary; integration tests drive via the CLI surface
`mouse` | private | Mouse event router — same reasoning as `keys`
`testutil` | pub(crate), `#[cfg(test)]` | Shared synthetic `DiskEntry` / `App` builders

## ?KEY DESIGN DECISIONS

#### Background telemetry, foreground render

Sysinfo refresh runs on a dedicated thread behind `Arc<Mutex<SysStats>>`. The render path takes the lock only long enough to `.clone()` into a local. No probe ever blocks the UI thread — slow filesystems return `None` after a deadline and the UI paints the last known good value.

#### 30 builtin themes, not 6

Each palette is a hand-tuned six-channel struct, not a hue rotation. Letting the user cycle by feel beats a single palette knob. The `color_mode_next_cycles_full_palette_exactly` test pins the cycle so adding a variant is a forced choice, not an accident.

#### Verbose test filenames

`tests/scan_directory_two_hundred_sixty_nine_files_integration.rs` reads worse than `scan_269.rs` but is self-describing in CI output. 1,336 such files mean grep-ability matters more than typing.

#### Every flag has a `--no-flag` inverse

Saved config is the baseline; one-off invocations need to be able to force-override in either direction without rewriting the file. `--no-tooltips` defeats a `show_tooltips = true` config without saving over it.

#### Probe results cached, never re-fetched mid-tick

SMART status, I/O byte counters, NFS latency all cache per refresh interval. Re-asking IOKit or shelling to `diskutil` on every paint would tank the render loop — refresh is a tick boundary, not a per-frame call.

#### Drill-down is a separate `ViewMode`

Rather than overlay the directory explorer on the disk list, drill-down swaps `ViewMode::Disks` → `ViewMode::DrillDown`. Cleaner mouse hit-tests, simpler scroll-wheel semantics, no z-order ambiguity. `Esc` restores the disk view; `Backspace` walks up the breadcrumb.

#### Inline tests ≈ production code

50 / 50 split inside `src/` (6,763 prod vs 6,862 test lines). Each module owns its inline coverage; cross-module behavior lives in `tests/`. Net 49,922 integration lines on top of 13,625 src.

#### No async runtime

Threaded refresh + bounded blocking probes (with deadlines) cover the workload without dragging in tokio. The binary stays small (release-stripped LTO), startup is instant, and the dependency tree stops at 269 crates instead of 400+.

## ~BENCHMARK SUITE

`benches/bench.rs` (344 lines, `harness = false`) drives a Criterion suite over the hot paths: byte formatting, uptime formatting, mount-name truncation, column-width math, palette resolve, gradient interpolation, time conversion, disk / sys-stat collection, config serde, and the sort / filter / render pipelines at 10 / 50 / 200 disks. Run with `cargo bench`; HTML reports land in `target/criterion/`.

Benchmark group | Covers | Representative time
--- | --- | ---
`format_bytes` | Human / GiB / MiB / Bytes pretty-printing | ~20–69 ns
`format_uptime` | Duration-to-string (minutes … days) | ~21–39 ns
`truncate_mount` | Mount-name compaction at widths 8 / 32 | ~27–97 ns
`mount_col_width` / `right_col_width_static` | Column-width layout math | ~540–590 ps
`palette` / `gradient_color_at` | Theme channel resolve + gradient lerp | ~0.7–2.4 ns
`epoch_to_local` / `chrono_now` | Epoch-to-local clock conversion | ~300–427 ns
`collect` | `collect_disk_entries` / `collect_sys_stats` | ~3.2–3.8 µs
`prefs_serde` | TOML serialize / deserialize of `Prefs` | ~4.6–5.3 µs
`sort_disks` | `by_name` / `by_pct` / `by_size` at 10 / 50 / 200 | ~242 ns–7.9 µs
`filter_disks` | Substring match / no-match at 10 / 50 / 200 | ~151 ns–5.9 µs
`format_all_disks` | Full render pass at 10 / 50 / 200 | ~1.5–15.7 µs

Times are Criterion-measured on Apple Silicon (M-series) and reproduced verbatim from the README; absolute figures vary by hardware. The point is the shape: every per-frame path is sub-microsecond, every per-tick path is single-digit microseconds, so the render loop is never the bottleneck.

## =CI & RELEASE

GitHub Actions gates every push, pull request, and merge-queue batch to `main`. All jobs run `--locked` so CI resolves the exact dependency graph committed in `Cargo.lock`; concurrent runs for a branch are cancelled when a newer commit lands.

Job | Command | Runners
--- | --- | ---
`Check` | `cargo check --locked --all-targets` | ubuntu + macos
`Test` | `cargo test --locked --lib`, `--tests`, `--doc` | ubuntu + macos
`Format` | `cargo fmt --all --check` | ubuntu
`Clippy` | `cargo clippy --locked --all-targets -- -D warnings` | ubuntu
`Doc` | `cargo doc --locked --no-deps` (`RUSTDOCFLAGS=-D warnings`) | ubuntu

The release workflow builds on tag push for six targets — `aarch64-apple-darwin`, `x86_64-apple-darwin`, `x86_64-unknown-linux-gnu`, `aarch64-unknown-linux-gnu` (cross-compiled), and static `x86_64` / `aarch64` musl variants — then publishes a GitHub release and regenerates the Homebrew formula in `MenkeTechnologies/homebrew-menketech`. To match CI locally before pushing:

    cargo fmt --all --check \
      && cargo clippy --locked --all-targets -- -D warnings \
      && cargo test --locked --lib \
      && cargo test --locked --tests \
      && cargo test --locked --doc

## .REPOSITORY

- Source — github.com/MenkeTechnologies/storageshower
- Crate — crates.io/crates/storageshower
- Homebrew — `brew tap MenkeTechnologies/menketech`
- API docs — docs.rs/storageshower
- Issues — github.com/MenkeTechnologies/storageshower/issues
- Docs hub — index.html (install, keys, CLI flags, themes)