1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
//! Definitions of common behavior shared amongst all different prompt types.
use crate::{error::InquireResult, input::InputActionResult, ui::CommonBackend, InquireError};
use super::action::{Action, InnerAction};
/// Represents the result of an action on the prompt.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum ActionResult {
/// The action resulted in a state change that requires the prompt to be
/// re-rendered.
NeedsRedraw,
/// The action either didn't result in a state change or the state
/// change does not require a redraw.
Clean,
}
impl ActionResult {
pub fn merge(self, other: Self) -> Self {
match (self, other) {
(Self::NeedsRedraw, _) | (_, Self::NeedsRedraw) => Self::NeedsRedraw,
(Self::Clean, Self::Clean) => Self::Clean,
}
}
/// Returns whether the action requires a redraw.
pub fn needs_redraw(&self) -> bool {
matches!(self, Self::NeedsRedraw)
}
}
impl From<InputActionResult> for ActionResult {
fn from(value: InputActionResult) -> Self {
if value.needs_redraw() {
Self::NeedsRedraw
} else {
Self::Clean
}
}
}
/// Shared behavior among all different prompt types.
pub trait Prompt<Backend>
where
Backend: CommonBackend,
Self: Sized,
{
type Config;
type InnerAction: InnerAction<Config = Self::Config>;
type Output;
/// Prompt header rendered to the user.
fn message(&self) -> &str;
/// Returns the underlying settings of the prompt, used, among other
/// goals, to parse a key event into a prompt action.
///
/// For example, a prompt might be configured to have vim mode enabled
/// or disabled, which affects how certain key events are parsed into
/// actions to the prompt.
fn config(&self) -> &Self::Config;
/// Hook called when a prompt is finished. Returns a string
/// to be rendered to the user as the final submission to the prompt.
///
/// # Arguments
///
/// * `answer` - Answer returned by the prompt.
fn format_answer(&self, answer: &Self::Output) -> String;
/// Hook called when a prompt is first started, before the first
/// draw happens.
fn setup(&mut self) -> InquireResult<()> {
Ok(())
}
/// Hook called when an input to cancel the prompt is triggered.
///
/// Returns whether the prompt can be terminated.
fn pre_cancel(&mut self) -> InquireResult<bool> {
Ok(true)
}
/// Hook called when the user submits the answer to the prompt.
///
/// On success, it should return `Some(ReturnType)` when the user
/// submission is valid and the prompt can graciously return.
///
/// If the user submission is invalid or should be rejected for some reason,
/// this method should return `Ok(None)`.
///
/// On `Err(*)`, the prompt is teared down.
fn submit(&mut self) -> InquireResult<Option<Self::Output>>;
/// Entrypoint for any business logic for the prompt. Returns the result
/// of the action. If the result is `Clean`, the prompt will
/// not be re-rendered.
///
/// On the usual path, users' key presses are parsed into prompt actions,
/// which are then submitted to this method to be handled.
///
/// On testing scenarios, developers might provide a stream of actions
/// to the prompt, which will then be submitted to this method just the same.
fn handle(&mut self, action: Self::InnerAction) -> InquireResult<ActionResult>;
/// Hook called for the rendering of the prompt UI.
///
/// The implementation should **not** call neither `frame_setup` or
/// `frame_finish` methods of the underlying backend, as this is handled
/// by the top-level prompt method.
fn render(&self, backend: &mut Backend) -> InquireResult<()>;
/// Top-level implementation of a prompt's flow.
///
/// This should not be reimplemented by types that implement this trait,
/// unless the situation really warrants it.
fn prompt(mut self, backend: &mut Backend) -> InquireResult<Self::Output> {
self.setup()?;
let mut last_handle = ActionResult::NeedsRedraw;
let final_answer = loop {
if last_handle.needs_redraw() {
backend.frame_setup()?;
self.render(backend)?;
backend.frame_finish(false)?;
last_handle = ActionResult::Clean;
}
let key = backend.read_key()?;
let action = Action::from_key(key, self.config());
if let Some(action) = action {
last_handle = match action {
Action::Submit => {
if let Some(answer) = self.submit()? {
break answer;
}
ActionResult::NeedsRedraw
}
Action::Cancel => {
let pre_cancel_result = self.pre_cancel()?;
if pre_cancel_result {
backend.frame_setup()?;
backend.render_canceled_prompt(self.message())?;
backend.frame_finish(true)?;
return Err(InquireError::OperationCanceled);
}
ActionResult::NeedsRedraw
}
Action::Interrupt => return Err(InquireError::OperationInterrupted),
Action::Inner(inner_action) => self.handle(inner_action)?,
};
}
};
let formatted = self.format_answer(&final_answer);
backend.frame_setup()?;
backend.render_prompt_with_answer(self.message(), &formatted)?;
backend.frame_finish(true)?;
Ok(final_answer)
}
}