rskit_cli/prompt/mod.rs
1//! Interactive prompts for guided CLI flows.
2//!
3//! A cohesive input layer beside [`crate::render`] and [`crate::theme`]: it asks
4//! a question plus a fixed set of [`Choice`]s and returns a typed answer, reusing
5//! the resolved [`Palette`](crate::theme::Palette) and [`Glyphs`](crate::theme::Glyphs)
6//! so styling honours `NO_COLOR`, TTY, and UTF-8 detection exactly like the rest
7//! of the CLI layer.
8//!
9//! Prompts speak through a [`Terminal`] seam rather than raw stdio, so the same
10//! question renders as a numbered list over a pipe, as a live arrow-key widget on
11//! a TTY, or as a scripted sequence in a test — without the calling code changing.
12//!
13//! # Modules
14//!
15//! - [`choice`] — [`Choice`] and its stable [`ChoiceId`].
16//! - [`mode`] — [`PromptMode`] interactive/non-interactive resolution.
17//! - [`key`] — the [`Key`] keystroke vocabulary for key-driven terminals.
18//! - [`validate`] — the [`Validator`] trait for re-asking on invalid input.
19//! - [`terminal`] — the [`Terminal`] seam: line, rich (raw-mode), and scripted media.
20//! - [`render`] — shared choice/frame rendering used by every terminal.
21//! - [`kinds`] — one behavioural module per question type.
22//! - [`prompter`] — the [`Prompter`] driver and its question methods.
23//!
24//! # Interaction models
25//!
26//! One set of prompt-kind logic drives three realities, chosen automatically:
27//!
28//! - a **line** terminal (cooked stdio) prints a numbered list and reads a typed
29//! answer — always available, dependency-free, and pipe-friendly;
30//! - a **rich** terminal (raw mode, behind the `interactive` feature) reads
31//! arrow keys and space to drive live radio and checkbox lists;
32//! - a **scripted** terminal replays canned keys or lines for deterministic tests.
33//!
34//! [`Prompter::from_env`] selects rich when a TTY is present and the feature is
35//! compiled, else line; tests bind a
36//! [`ScriptedTerminal`] directly.
37//!
38//! # Non-interactive fallback
39//!
40//! The driver resolves its behaviour once, up front, from a [`PromptMode`]:
41//!
42//! - [`PromptMode::Interactive`] — render prompts and read answers.
43//! - [`PromptMode::NonInteractive`] — never block: each question resolves to its
44//! declared default (the `recommended` [`Choice`] for a selection, the supplied
45//! default for a confirm/text). A required question with no default is a typed
46//! [`AppError`](rskit_errors::AppError) rather than an invented answer or a hang.
47
48pub mod choice;
49pub mod key;
50pub mod kinds;
51pub mod mode;
52pub mod prompter;
53pub mod render;
54pub mod terminal;
55pub mod validate;
56
57pub use choice::{Choice, ChoiceId};
58pub use key::Key;
59pub use mode::PromptMode;
60pub use prompter::Prompter;
61pub use terminal::{Capabilities, LineTerminal, ScriptedTerminal, Terminal};
62pub use validate::{Validation, Validator, non_empty};
63
64#[cfg(feature = "interactive")]
65pub use terminal::RichTerminal;