The app around the table: the event loop and the terminal, background jobs, the
overlay and the dialogs it opens, and the keys of the screens without a directory of
their own.
Writing the view to an export file. run takes the plan to a committed file:
uncompressed CSV and Parquet stream through Polars’ sink into the OutputFile;
everything else (compressed CSV, JSON, NDJSON, IPC, Avro, or no streaming engine)
collects and encodes. The destination changes only at commit, after the last
byte, encoder finish and flush succeed. Streaming bounds the export, not the plan: a
sort, group-by or join still gathers its input first.
Find in the table: / (or f) asks for a pattern, n and N move between matching
cells. While typing, matches among rows on hand light up and are counted (3 on screen) without reading; Ctrl+G keeps matching rows as a sidebar filter. The view
(query, filters, sort, shown columns) is searched as is and never changed; only the
cursor moves. Searches run as jobs reading a window at a time from the buffer
outward, finding only the next match, so the total is known only as far as finds
have walked.
Binary formats described by specs: one TOML file per format, on a search path. A
spec names its format (acme.l2feed), says which files match (glob, magic, header
values), and lays out header and records. Specs are data (no expressions or code:
fields refer to earlier ones by name), and every size read from a file is bounded.
Decoding is crate::formats::fixed_records’ for fixed-size records and
crate::formats::framed_records’ otherwise; files reads a directory of one spec’s
files. This module turns a spec and a file into that reader’s columns.
The log file, and keeping stray stderr off the screen: while the TUI runs, fd 2
points at the log, Polars warnings go through log, and non-fatal errors (cache,
history) land here.
What datui noticed about a dataset while doing what it was already doing. A note
never costs its own request or scan (all come from footers the schema and count
needed), is never styled as an alarm, and states its basis, so “in 1 of 3 files” is
never read as a claim about files not looked at. One claim per note, and one
function (out_of) deciding the only ratio any note states.
Display-time number formatting: digit grouping, decimal separator, optional fixed
float precision. Display only: exports, queries, filters, views and group-by keys use
raw values. Runs per visible cell per frame, so it allocates nothing beyond the
caller’s String: integers are written digit by digit with inline separators,
NumberFormat::width_i64 measures arithmetically, and per-column decisions resolve
to a CellFormatter once per column per frame.
Polars operations that panic on a date or datetime past the calendar’s range
(a sentinel like i64::MIN + 1 microseconds): a cast to text, dt.to_string,
and the date parts that go through a calendar date (dt.date with a zone,
dt.month_start, dt.truncate, …). Each panics for the whole column, even
when one row holds such a value.
Keeping untrusted text (cells, column names, filenames, parser errors) from becoming
terminal commands: \x1b]52;c;...\x07 in a cell writes the clipboard, \x1b[2J wipes
the screen. ratatui’s Buffer::set_stringn drops control graphemes, but Span
and Line rendering append zero-width graphemes (ESC included) to the previous cell,
and crossterm prints cells unfiltered. Rather than guard every Span site, one sweep
runs at the end of App::render over the finished buffer.
Typed text read as a column’s own type, so a filter or Data Quality partition stays
a plain col op lit comparison (comparing as text fails for dates, or casts every
row, blinding Parquet statistics and SQLite to the predicate).
A completion flash (the Feedback rules’ second rung): one plain sentence on
the footer, cleared by the next keypress or after two seconds,
whichever comes first. Every screen’s completions go here, the home screen’s
included. The home screen’s own status line beside the filter is for what a
key could not do and why, which has to survive until it is read.
Rows a sampled Data Quality run read, and what decided which rows they were: its
acquisition identity. A run or a drill that names the same rows cuts these instead
of reading.
What a directory read found about itself, filled by the pass that picks files and
reader and carried back for the Notes: what datui did, not what the data is (the
footer notes, written later, are that).
A remote file downloaded without asking: one the built-in catalog lists, at most
limit bytes by what the server says or, when it says nothing, by the catalog.
What Enter will do on the highlighted row, for the footer and the
details pane. A prediction of App::home_open_selected;
test_the_bar_says_what_enter_will_really_do keeps the two in agreement.
Memory Data Quality keeps for the session: the rows its sampled runs read and the
reports they made. Past it, reports that retained rows can remake go first, then
the oldest rows, then the oldest other reports; the newest of each always stays.
The exit status when a signal ended the session run returned from: 128 + n
for SIGTERM, SIGHUP or SIGINT (as datui_cli::exit), and on Windows a closed
console’s status.