Skip to main content

cli_ui/prompt/
mod.rs

1//! Interactive prompts — clack/prompts-style.
2//!
3//! # Hello, prompt
4//!
5//! ```no_run
6//! use cli_ui::prompt::{intro, outro, text, confirm, OnCancel};
7//!
8//! fn main() {
9//!     intro("Set up a new project");
10//!
11//!     let name = text("What's your name?")
12//!         .placeholder("Anya")
13//!         .run()
14//!         .or_cancel("Cancelled.");
15//!
16//!     let public = confirm("Make it public?")
17//!         .default(true)
18//!         .run()
19//!         .or_cancel("Cancelled.");
20//!
21//!     outro(format!("Hello {name}! profile is {}.",
22//!         if public { "public" } else { "private" }));
23//! }
24//! ```
25//!
26//! # The mental model
27//!
28//! Every prompt is one of three things:
29//!
30//! 1. **A builder** — `text("…")`, `select("…")`, etc. Chain `.placeholder()`,
31//!    `.default()`, `.validate()`, `.option()` etc. Always finish with `.run()`.
32//! 2. **A frame helper** — [`intro`], [`outro`], [`note`], [`cancel`],
33//!    [`boxed::boxed`]. They print a single framed line/block and return
34//!    immediately. Use them to surround your prompt flow.
35//! 3. **A long-running task display** — [`spinner`](fn@spinner), [`progress`],
36//!    [`tasks`](fn@tasks), [`task_log`](fn@task_log). These run in the foreground
37//!    while non-prompt work happens.
38//!
39//! Everything renders to **stderr** so your prompts don't pollute pipes.
40//!
41//! # The four categories at a glance
42//!
43//! ## Question prompts
44//!
45//! | Prompt            | Picks                                |
46//! |-------------------|--------------------------------------|
47//! | [`text`](fn@text)          | A line of text                       |
48//! | [`secret`]        | A masked line (password, API key)    |
49//! | [`multiline`](fn@multiline)     | Several lines                        |
50//! | [`confirm`](fn@confirm)       | Yes / No                             |
51//! | [`select`](fn@select)        | One option from a list               |
52//! | [`multiselect`](fn@multiselect)   | Many options from a list             |
53//! | [`groupmultiselect`](fn@groupmultiselect) | Many options from grouped lists |
54//! | [`autocomplete`](fn@autocomplete)  | One option, filtered by typing       |
55//! | [`select_key`](fn@select_key)    | One option by single keypress        |
56//! | [`date::date`]    | A `yyyy-mm-dd` date                  |
57//! | [`path::path`]    | A filesystem path with completion    |
58//!
59//! ## Framing
60//!
61//! [`intro`] / [`outro`] / [`note`] / [`cancel`] / [`boxed::boxed`] /
62//! [`log`] / [`stream`].
63//!
64//! ## Live work
65//!
66//! [`spinner`](fn@spinner) / [`progress`] / [`tasks`](fn@tasks) / [`task_log`](fn@task_log).
67//!
68//! ## Composition
69//!
70//! [`group::group`] runs many prompts sequentially and collects their answers.
71//!
72//! # Validation
73//!
74//! Validators are promoted to the prompt root so they import like any
75//! other constructor:
76//!
77//! ```no_run
78//! use cli_ui::prompt::{text, min_chars, has_upper, has_digit};
79//!
80//! let pw = text("Password")
81//!     .rule(min_chars(12).and(has_upper()).and(has_digit()))
82//!     .run();
83//! ```
84//!
85//! # Customisation
86//!
87//! Theme everything in one call via [`settings::update_colors`]:
88//!
89//! ```
90//! cli_ui::prompt::settings::update_colors(|c| {
91//!     c.accent = anstyle::Style::new()
92//!         .fg_color(Some(anstyle::Color::Ansi(anstyle::AnsiColor::Magenta)))
93//!         .bold();
94//! });
95//! ```
96//!
97//! # Cancellation
98//!
99//! When the user hits Ctrl-C or Esc, `.run()` returns
100//! `Err(PromptError::Interrupted)`. The [`OnCancel`] extension trait turns
101//! that into a clean exit:
102//!
103//! ```no_run
104//! use cli_ui::prompt::{text, OnCancel};
105//! let name = text("Your name").run().or_cancel("Aborted — see you next time.");
106//! ```
107//!
108//! # Extending
109//!
110//! Implement [`core::Prompt`] to ship your own prompt — see the existing
111//! prompts in this module for templates. The runner handles raw mode,
112//! cleanup, validation transitions, and the answered redraw for you.
113//!
114//! # One-line import
115//!
116//! For quick scripts and examples, glob-import the prelude:
117//!
118//! ```
119//! use cli_ui::prompt::prelude::*;
120//! ```
121
122pub mod core;
123mod cursor;
124mod engine;
125mod error;
126mod limit_options;
127pub mod settings;
128mod theme;
129pub mod validate;
130
131mod render;
132
133pub mod autocomplete;
134pub mod boxed;
135pub mod confirm;
136pub mod date;
137pub mod group;
138pub mod groupmultiselect;
139pub mod log;
140pub mod multiline;
141pub mod multiselect;
142pub mod path;
143pub mod progress_bar;
144pub mod select;
145pub mod select_key;
146pub mod spinner;
147pub mod stream;
148pub mod task_log;
149pub mod tasks;
150pub mod text;
151
152pub use autocomplete::{autocomplete, AutocompleteSelected};
153pub use boxed::{boxed, Align as BoxAlign, BoxOptions};
154pub use confirm::confirm;
155pub use date::{date, Date, DatePrompt};
156pub use group::group;
157pub use groupmultiselect::{groupmultiselect, GroupItem, GroupSelected};
158pub use multiline::multiline;
159pub use multiselect::{multiselect, MultiSelected};
160pub use path::path;
161pub use progress_bar::{progress, Progress, Style as ProgressStyle};
162pub use select::{select, Selected};
163pub use select_key::{select_key, KeySelected};
164pub use spinner::{spinner, Spinner};
165pub use task_log::{task_log, TaskLog};
166pub use tasks::{tasks, Task};
167pub use text::{secret, text};
168
169pub use error::{is_cancel, OnCancel, PromptError, Result};
170
171// ── Promoted from `validate::*` ───────────────────────────────────────────────
172//
173// Every common rule and the `Validator` type sit at the prompt root so users
174// can compose without the `validate::` prefix:
175//
176//     use cli_ui::prompt::{text, min_chars, has_upper};
177//     text("pw").rule(min_chars(8).and(has_upper())).run()?;
178//
179// The full `validate` module is still re-exported for users who want every
180// helper at once.
181pub use validate::{
182    alpha_only, alphanumeric, email, ends_with, exact_chars, float_between, forbid_chars,
183    has_digit, has_lower, has_special, has_upper, int_between, max_chars, min_chars, one_of,
184    only_chars, required, starts_with, word_count, words_between, Validate, Validator,
185};
186
187// ── Promoted from `settings::*` ──────────────────────────────────────────────
188//
189// The most common settings ops — colour overrides — at the prompt root so
190// users can theme without an extra module path:
191//
192//     use cli_ui::prompt::update_colors;
193//     update_colors(|c| c.accent = magenta_bold);
194pub use settings::{colors, set_colors, update_colors, Colors};
195
196/// Glob-importable shortcut for the most common entry points.
197///
198/// ```
199/// use cli_ui::prompt::prelude::*;
200/// ```
201///
202/// Re-exports every prompt constructor, the frame helpers (`intro`, `outro`,
203/// `note`, `cancel`), `OnCancel`, the validator rules library, and the
204/// colour theme ops. Designed for quick scripts; production code can stick
205/// with the explicit imports.
206pub mod prelude {
207    pub use super::{
208        autocomplete, boxed, cancel, confirm, date, group, groupmultiselect, intro, log, multiline,
209        multiselect, note, outro, path, progress, secret, select, select_key, spinner, stream,
210        task_log, tasks, text,
211    };
212    pub use super::{OnCancel, PromptError, Result};
213    // Rules library — composable predicates and the Validator type.
214    pub use super::{
215        alpha_only, alphanumeric, email, ends_with, exact_chars, float_between, forbid_chars,
216        has_digit, has_lower, has_special, has_upper, int_between, max_chars, min_chars, one_of,
217        only_chars, required, starts_with, word_count, words_between, Validate, Validator,
218    };
219    // Colour theme.
220    pub use super::{colors, set_colors, update_colors, Colors};
221    // Full validate module for users who want everything at once.
222    pub use super::validate;
223}
224
225// ── Frame helpers (clack-style) ───────────────────────────────────────────────
226
227/// Print `┌  message` — opens a connected prompt session.
228pub fn intro(message: impl AsRef<str>) {
229    eprintln!("{}", theme::intro(message.as_ref()));
230}
231
232/// Print `└  message` — closes a prompt session.
233pub fn outro(message: impl AsRef<str>) {
234    eprintln!("{}", theme::outro(message.as_ref()));
235}
236
237/// Print a framed informational note between prompts.
238pub fn note(title: impl AsRef<str>, body: impl AsRef<str>) {
239    eprintln!("{}", theme::note(title.as_ref(), body.as_ref()));
240}
241
242/// Print `■  message` — used after a prompt was cancelled / interrupted.
243pub fn cancel(message: impl AsRef<str>) {
244    eprintln!("{}", theme::cancel(message.as_ref()));
245}