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