1use crossterm::{
6 event::{KeyCode, KeyEvent, KeyModifiers},
7 style::{style, Color, PrintStyledContent},
8};
9use std::cmp;
10
11pub enum Figures {
13 ArrowUp,
14 ArrowDown,
15 ArrowLeft,
16 ArrowRight,
17 RadioOn,
18 RadioOff,
19 Tick,
20 Cross,
21 Ellipsis,
22 PointerSmall,
23 Line,
24 Pointer,
25}
26impl Figures {
27 #[cfg(windows)]
28 pub fn as_str(&self) -> &'static str {
29 match self {
30 Figures::ArrowUp => "↑",
31 Figures::ArrowDown => "↓",
32 Figures::ArrowLeft => "←",
33 Figures::ArrowRight => "→",
34 Figures::RadioOn => "(*)",
35 Figures::RadioOff => "( )",
36 Figures::Tick => "√",
37 Figures::Cross => "×",
38 Figures::Ellipsis => "...",
39 Figures::PointerSmall => "»",
40 Figures::Line => "─",
41 Figures::Pointer => ">",
42 }
43 }
44 #[cfg(not(windows))]
45 pub fn as_str(&self) -> &'static str {
46 match self {
47 Figures::ArrowUp => "↑",
48 Figures::ArrowDown => "↓",
49 Figures::ArrowLeft => "←",
50 Figures::ArrowRight => "→",
51 Figures::RadioOn => "◉",
52 Figures::RadioOff => "◯",
53 Figures::Tick => "✔",
54 Figures::Cross => "✖",
55 Figures::Ellipsis => "…",
56 Figures::PointerSmall => "›",
57 Figures::Line => "─",
58 Figures::Pointer => "❯",
59 }
60 }
61}
62
63#[derive(Eq, PartialEq, Debug)]
65pub enum PromptState {
66 Created,
68 Running,
70 Validate,
72 Aborted,
74 Success,
76}
77impl Default for PromptState {
78 fn default() -> PromptState {
79 PromptState::Created
80 }
81}
82impl PromptState {
83 pub fn is_done(&self) -> bool {
84 *self == PromptState::Aborted || *self == PromptState::Success
85 }
86}
87
88pub fn is_abort_event(event: KeyEvent) -> bool {
92 match event {
93 KeyEvent {
94 modifiers: KeyModifiers::CONTROL,
95 code: KeyCode::Char('c'),
96 } => true,
97 KeyEvent {
98 modifiers: KeyModifiers::CONTROL,
99 code: KeyCode::Char('d'),
100 } => true,
101 KeyEvent {
102 modifiers,
103 code: KeyCode::Esc,
104 } if modifiers == KeyModifiers::empty() => true,
105 _ => false,
106 }
107}
108
109pub fn print_state_icon(state: &PromptState) -> PrintStyledContent<&'static str> {
111 PrintStyledContent(match state {
112 PromptState::Aborted => style(Figures::Cross.as_str()).with(Color::Red),
113 PromptState::Success => style(Figures::Tick.as_str()).with(Color::Green),
114 _ => style("?").with(Color::Magenta),
115 })
116}
117
118pub fn print_input_icon(state: &PromptState) -> PrintStyledContent<String> {
120 PrintStyledContent(
121 style(match state {
122 PromptState::Aborted => "".to_string(),
123 PromptState::Success => format!("{} ", Figures::Ellipsis.as_str()),
124 _ => format!("{} ", Figures::PointerSmall.as_str()),
125 })
126 .with(Color::Grey),
127 )
128}
129
130pub fn calc_entries(current: usize, total: usize, limit: usize) -> (usize, usize) {
134 let start_index = cmp::min(
135 total.checked_sub(limit).unwrap_or(0),
136 current.checked_sub(limit / 2).unwrap_or(0),
137 );
138 let end_index = cmp::min(start_index + limit, total);
139 (start_index, end_index)
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 #[cfg(windows)]
148 fn write_all() {
149 let figures = [
150 Figures::ArrowUp,
151 Figures::ArrowDown,
152 Figures::ArrowLeft,
153 Figures::ArrowRight,
154 Figures::RadioOn,
155 Figures::RadioOff,
156 Figures::Tick,
157 Figures::Cross,
158 Figures::Ellipsis,
159 Figures::PointerSmall,
160 Figures::Line,
161 Figures::Pointer,
162 ];
163 let mut s = String::from("");
164 for figure in figures.iter() {
165 s.push_str(figure.as_str());
166 }
167 assert_eq!(s, "↑↓←→(*)( )√×...»─>");
168 }
169
170 #[test]
171 #[cfg(not(windows))]
172 fn write_all() {
173 let figures = [
174 Figures::ArrowUp,
175 Figures::ArrowDown,
176 Figures::ArrowLeft,
177 Figures::ArrowRight,
178 Figures::RadioOn,
179 Figures::RadioOff,
180 Figures::Tick,
181 Figures::Cross,
182 Figures::Ellipsis,
183 Figures::PointerSmall,
184 Figures::Line,
185 Figures::Pointer,
186 ];
187 let mut s = String::from("");
188 for figure in figures.iter() {
189 s.push_str(figure.as_str());
190 }
191 assert_eq!(s, "↑↓←→◉◯✔✖…›─❯");
192 }
193}