Skip to main content

cli_ui/prompt/core/
mod.rs

1//! Core abstractions every prompt implements.
2//!
3//! A prompt is three small things:
4//!
5//! ```text
6//! impl Prompt for MyPrompt {
7//!     type Output = String;
8//!     fn handle(&mut self, key: Key) -> Step<String> { ... }
9//!     fn render(&self, ctx: RenderCtx) -> Frame      { ... }
10//!     fn render_answered(&self, v: &String) -> Frame { ... }
11//! }
12//! ```
13//!
14//! The generic [`run`] function in `core::runner` owns everything
15//! else: raw mode, the key loop, line-count bookkeeping, the validate
16//! → success transition, the answered redraw, and Ctrl+C cleanup. Prompts
17//! never touch crossterm or stderr directly.
18
19mod frame;
20mod runner;
21mod step;
22
23pub use frame::{Frame, RenderCtx};
24pub use runner::run;
25pub use step::Step;
26
27pub use super::engine::Key;
28use super::error::Result;
29
30/// The contract every interactive prompt implements.
31pub trait Prompt {
32    /// The value returned to the caller on successful submission.
33    type Output;
34
35    /// Apply a key press to the prompt's internal state. Return:
36    ///
37    /// * [`Step::Continue`] — state changed, but we're not done.
38    /// * `Step::Submit(v)` — done, hand `v` back to the caller.
39    /// * `Step::Reject(msg)` — block submission, show `msg` as an error
40    ///   below the input. The runner clears the error when the next key
41    ///   arrives.
42    /// * [`Step::Cancel`] — user asked to abort (Esc / Ctrl-C).
43    fn handle(&mut self, key: Key) -> Step<Self::Output>;
44
45    /// Render the prompt's current state as one frame (a `Vec<String>`,
46    /// one entry per terminal row). The runner takes care of writing the
47    /// frame and clearing it on the next iteration — prompts never call
48    /// `eprintln!` themselves.
49    fn render(&self, ctx: RenderCtx) -> Frame;
50
51    /// Render the prompt's post-submission display. Typically the
52    /// `◇  question / │  value / │` triple from `theme::answered`.
53    fn render_answered(&self, value: &Self::Output) -> Frame;
54
55    /// Optional non-interactive fallback for piped/CI environments. The
56    /// default is to refuse — most prompts override this with a numeric
57    /// or line-buffered alternative.
58    fn run_fallback(self) -> Result<Self::Output>
59    where
60        Self: Sized,
61    {
62        Err(super::error::PromptError::Interrupted)
63    }
64}