Skip to content

What rtb-tui does not do

Everything on this page is a deliberate absence or a known gap at version 0.6.3, not a defect to report. If you are about to work around one of these, the workaround is the right move.

It is not a full-screen terminal UI

Despite the name, this crate draws no panels, no layouts and no widgets. It does not enter the alternate screen, does not take over the terminal, does not read key events, and has no event loop. There is no ratatui in its dependency graph.

What it has is three things a line-oriented CLI needs: a prompt sequence, two output renderers and a status line. If you want a dashboard, a file browser or anything that repaints a region, this is the wrong crate and ratatui is the right one.

There are no Cargo features

Cargo.toml declares no [features] table at all. Consequences:

  • default-features = false does nothing.
  • You cannot take the wizard without also compiling the render helpers and the spinner.
  • You cannot drop miette, serde_json or tabled from your dependency graph while using any part of the crate.

Depending on rtb-tui means taking all three components and all eight runtime dependencies. miette with its fancy feature is the largest of them, and it is not optional.

The wizard only moves one step at a time

Back navigation decrements the step index by one. There is no jump-to-step, no "return to summary", no skipping forward over a step already answered, and no conditional branching between steps — the step list is a Vec walked in registration order.

A wizard that needs to branch has to model the branch inside a step: one step that asks a question and then asks the follow-ups itself, rather than two steps the driver chooses between.

The wizard keeps no history and rolls nothing back

There is no snapshot of the state at each step and no undo. Going back re-runs the earlier step against the state as it stands now, including anything written by later steps.

So a step that appends — state.items.push(..) — will append again every time the user visits it. Steps must be idempotent, and nothing in the type system enforces that.

There is also no persistence: a wizard cannot be saved half-finished and resumed later. Wizard::run either returns the completed state or an error.

The builder panics instead of failing to compile

WizardBuilder::build panics with Wizard::builder requires .initial(...) if you never called initial. It is not a typestate builder and it does not return a Result — the mistake surfaces at runtime.

The rustdoc claims a typed replacement arrived in v0.2. At 0.6.3 it has not.

Table output has one style and no options

render_table applies Style::psql() and nothing else. There is no theming, no colour, no column-width control, no sorting, no pagination and no way to change the separator. See Why the table style is not configurable.

Nested data is the practical limit. Serialize handles a struct inside a struct; Tabled does not without a per-field attribute telling it how to flatten. A row type built for JSON will not always render as a table.

There are only two output formats

Text and JSON. No YAML, no CSV, no TSV, no templating, no newline-delimited JSON. Adding a third format means writing it in your own crate — the helpers are not extensible and there is no renderer trait to implement.

The spinner does not spin

One static glyph, redrawn only when you call set_message. No frame animation, no timer, no background task, no percentage, no progress bar, no elapsed-time display, no multi-line or nested progress. See Why the spinner does not animate.

It also writes only to stderr, and that is not configurable — there is no way to point it at stdout or at an arbitrary writer.

The spinner cannot be tested against a real terminal

The TTY check reads the real console::Term::stderr() and there is no injection point. Under cargo test stderr is captured, so the spinner is always inert and the drawing path is never exercised by the suite. The tests assert that nothing panics, not that anything is rendered.

Errors do not chain

WizardError::Step stores the failing step's name and the underlying error's message, both as String. The original InquireError is dropped, so there is no source() to walk and no way to match on the original variant from outside the step.

RenderError::Json is the same shape — a stringified serde_json::Error, not the error itself.

The declared MSRV is not tested

Cargo.toml says rust-version = "1.82". rust-toolchain.toml pins 1.97.1, and every CI job uses the pin, so nothing in this repository ever compiles the crate on 1.82. Treat the MSRV as intent rather than a verified guarantee.

The crate knows nothing about your CLI framework

No command registration, no configuration loading, no logging, no dependency injection, no argument parsing. It takes a state type and a slice of rows and returns strings and errors. Wiring it to a flag, a config key or a log line is your code's job — and deny.toml bans a dependency back on rust-tool-base, so it will stay that way.