Skip to main content

prompts/
prompts.rs

1//! Interactive prompt examples covering every input widget. Run in a real
2//! terminal: `cargo run --example prompts --features fuzzy`.
3//!
4//! Each prompt runs in turn; pressing Esc (or Ctrl-C) cancels and ends the
5//! demo cleanly.
6
7use sparcli::event::{KeyCode, KeyPress};
8use sparcli::shortcut::{self, Shortcut};
9use sparcli::validate::{alnum, non_empty};
10use sparcli::{
11    Alert, Confirm, DatePicker, NumberInput, Outcome, PasswordInput,
12    Renderable, Rendered, Select, TextInput, Textarea,
13};
14
15#[cfg(feature = "fuzzy")]
16use sparcli::FuzzySelect;
17
18/// Unwraps a submitted value or ends the demo on cancellation.
19macro_rules! get {
20    ($prompt:expr) => {
21        match $prompt {
22            Outcome::Submitted(value) => value,
23            _ => return cancelled(),
24        }
25    };
26}
27
28fn main() -> sparcli::Result<()> {
29    footer_hint()?;
30
31    let name = get!(
32        TextInput::new("Your name?")
33            .placeholder("e.g. Alice")
34            .validate(non_empty())
35            .run()?
36    );
37
38    let _username = get!(
39        TextInput::new("Username?")
40            .char_filter(alnum())
41            .max_chars(16)
42            .suggestions(["alice", "albert", "bob", "carol"])
43            .history(["alice", "bob"])
44            .run()?
45    );
46
47    let _password = get!(PasswordInput::new("Password?").mask("•").run()?);
48
49    let age = get!(
50        NumberInput::new("Age? (try `= 20 + 2`)")
51            .range(0.0, 130.0)
52            .calculator()
53            .run()?
54    );
55
56    let _bio = get!(Textarea::new("Short bio (Ctrl-D to submit):").run()?);
57
58    let color = get!(
59        Select::new("Favorite color?")
60            .options(["red", "green", "blue"])
61            .run()?
62    );
63
64    let toppings = get!(
65        Select::new("Pick toppings (Space to toggle):")
66            .options(["cheese", "mushroom", "olive", "onion"])
67            .multi()
68            .run_multi()?
69    );
70
71    let _fruit = get!(pick_fruit()?);
72
73    let date = get!(DatePicker::new("Pick a date:").run()?);
74
75    let confirmed = matches!(
76        Confirm::new("Save everything?")
77            .default_yes()
78            .labels("Save", "Discard")
79            .run()?,
80        Outcome::Submitted(true)
81    );
82
83    if confirmed {
84        Alert::success(format!(
85            "Saved {name}, age {age:.0}, color #{color}, {} toppings, \
86             date {}-{:02}-{:02}.",
87            toppings.len(),
88            date.year,
89            date.month,
90            date.day,
91        ))
92        .print()?;
93    } else {
94        cancelled()?;
95    }
96    Ok(())
97}
98
99/// Runs the fuzzy picker when the feature is enabled.
100#[cfg(feature = "fuzzy")]
101fn pick_fruit() -> sparcli::Result<Outcome<usize>> {
102    FuzzySelect::new("Find a fruit:")
103        .options(["apple", "apricot", "banana", "cherry", "grape"])
104        .run()
105}
106
107/// Stand-in that submits a default when the `fuzzy` feature is disabled.
108#[cfg(not(feature = "fuzzy"))]
109fn pick_fruit() -> sparcli::Result<Outcome<usize>> {
110    Ok(Outcome::Submitted(0))
111}
112
113/// Prints a footer-style key hint line above the prompts.
114fn footer_hint() -> sparcli::Result<()> {
115    let shortcuts = [
116        Shortcut::new(KeyPress::new(KeyCode::Enter), 1, "submit"),
117        Shortcut::new(KeyPress::new(KeyCode::Esc), 2, "cancel"),
118    ];
119    Rendered::new(vec![shortcut::hint_line(&shortcuts)]).print()
120}
121
122/// Prints a cancellation notice.
123fn cancelled() -> sparcli::Result<()> {
124    Alert::warning("Cancelled.").print()
125}