romm_cli/tui/screens/setup_wizard/
input.rs1use anyhow::Result;
4use crossterm::event::{KeyCode, KeyEvent, KeyEventKind};
5
6use crate::config::normalize_romm_origin;
7use crate::core::download::validate_configured_download_directory;
8use crate::tui::path_picker::PathPickerEvent;
9
10use super::types::{AuthKind, SetupWizard, Step};
11
12impl SetupWizard {
13 fn add_char_url(&mut self, c: char) {
14 let pos = self.url_cursor.min(self.url.len());
15 self.url.insert(pos, c);
16 self.url_cursor = pos + 1;
17 }
18
19 fn del_char_url(&mut self) {
20 if self.url_cursor > 0 && self.url_cursor <= self.url.len() {
21 self.url.remove(self.url_cursor - 1);
22 self.url_cursor -= 1;
23 }
24 }
25
26 fn advance_from_auth_menu(&mut self) {
27 self.auth_kind = Self::auth_kind_from_index(self.auth_menu_selected);
28 self.step = match self.auth_kind {
29 AuthKind::Basic => Step::BasicUser,
30 AuthKind::Bearer => Step::Bearer,
31 AuthKind::ApiKey => Step::ApiHeader,
32 AuthKind::Pairing => {
33 self.pairing_cursor = self.pairing_code.len();
34 Step::PairingCode
35 }
36 };
37 }
38
39 fn advance_after_auth_credentials(&mut self) {
40 self.step = if self.skip_custom_console_paths {
41 Step::Summary
42 } else {
43 Step::CustomConsolePaths
44 };
45 }
46
47 fn advance_step(&mut self) -> Result<()> {
48 self.error = None;
49 match self.step {
50 Step::Url => {
51 if normalize_romm_origin(self.url.trim()).is_empty() {
52 self.error = Some("Enter a valid server URL".to_string());
53 return Ok(());
54 }
55 self.step = Step::Https;
56 }
57 Step::Https => {
58 self.step = Step::Download;
59 }
60 Step::Download => {}
61 Step::CustomConsolePaths => {
62 self.step = Step::Summary;
63 }
64 Step::AuthMenu => self.advance_from_auth_menu(),
65 Step::BasicUser => self.step = Step::BasicPass,
66 Step::BasicPass => self.advance_after_auth_credentials(),
67 Step::Bearer => self.advance_after_auth_credentials(),
68 Step::ApiHeader => self.step = Step::ApiKey,
69 Step::ApiKey => self.advance_after_auth_credentials(),
70 Step::PairingCode => self.advance_after_auth_credentials(),
71 Step::Summary => {}
72 }
73 Ok(())
74 }
75
76 pub fn handle_key(&mut self, key: &KeyEvent) -> Result<bool> {
77 if key.kind != KeyEventKind::Press {
78 return Ok(false);
79 }
80 if key.code == KeyCode::Esc {
81 return Ok(true); }
83
84 if self.testing {
85 return Ok(false);
86 }
87
88 match self.step {
89 Step::Url => match key.code {
90 KeyCode::Enter => {
91 let _ = self.advance_step();
92 }
93 KeyCode::Char(c) => self.add_char_url(c),
94 KeyCode::Backspace => self.del_char_url(),
95 KeyCode::Left if self.url_cursor > 0 => {
96 self.url_cursor -= 1;
97 }
98 KeyCode::Right if self.url_cursor < self.url.len() => {
99 self.url_cursor += 1;
100 }
101 _ => {}
102 },
103 Step::Https => match key.code {
104 KeyCode::Enter => {
105 let _ = self.advance_step();
106 }
107 KeyCode::Char(' ') => self.use_https = !self.use_https,
108 _ => {}
109 },
110 Step::Download => match self.download_picker.handle_key(key) {
111 PathPickerEvent::Confirmed(p) => {
112 self.error = None;
113 match validate_configured_download_directory(p.to_string_lossy().as_ref()) {
114 Ok(canonical) => {
115 self.download_picker
116 .set_path_text(canonical.display().to_string());
117 self.step = Step::AuthMenu;
118 }
119 Err(e) => {
120 self.error = Some(format!("{e:#}"));
121 }
122 }
123 }
124 PathPickerEvent::None => {}
125 },
126 Step::CustomConsolePaths => {
127 if key.code == KeyCode::Enter {
128 let _ = self.advance_step();
129 }
130 }
131 Step::AuthMenu => match key.code {
132 KeyCode::Up | KeyCode::Char('k') if self.auth_menu_selected > 0 => {
133 self.auth_menu_selected -= 1;
134 }
135 KeyCode::Down | KeyCode::Char('j') if self.auth_menu_selected < 3 => {
136 self.auth_menu_selected += 1;
137 }
138 KeyCode::Enter => {
139 let _ = self.advance_step();
140 }
141 _ => {}
142 },
143 Step::BasicUser => match key.code {
144 KeyCode::Tab => self.step = Step::BasicPass,
145 KeyCode::Enter => {
146 let _ = self.advance_step();
147 }
148 KeyCode::Char(c) => {
149 let pos = self.user_cursor.min(self.username.len());
150 self.username.insert(pos, c);
151 self.user_cursor = pos + 1;
152 }
153 KeyCode::Backspace
154 if self.user_cursor > 0 && self.user_cursor <= self.username.len() =>
155 {
156 self.username.remove(self.user_cursor - 1);
157 self.user_cursor -= 1;
158 }
159 KeyCode::Left if self.user_cursor > 0 => {
160 self.user_cursor -= 1;
161 }
162 KeyCode::Right if self.user_cursor < self.username.len() => {
163 self.user_cursor += 1;
164 }
165 _ => {}
166 },
167 Step::BasicPass => match key.code {
168 KeyCode::Tab => self.step = Step::BasicUser,
169 KeyCode::Enter => {
170 let _ = self.advance_step();
171 }
172 KeyCode::Char(c) => {
173 self.reuse_keyring_password = false;
174 self.password.push(c);
175 }
176 KeyCode::Backspace => {
177 self.password.pop();
178 }
179 _ => {}
180 },
181 Step::Bearer => match key.code {
182 KeyCode::Enter => {
183 let _ = self.advance_step();
184 }
185 KeyCode::Char(c) => {
186 self.reuse_keyring_bearer = false;
187 let pos = self.bearer_cursor.min(self.bearer_token.len());
188 self.bearer_token.insert(pos, c);
189 self.bearer_cursor = pos + 1;
190 }
191 KeyCode::Backspace
192 if self.bearer_cursor > 0 && self.bearer_cursor <= self.bearer_token.len() =>
193 {
194 self.bearer_token.remove(self.bearer_cursor - 1);
195 self.bearer_cursor -= 1;
196 }
197 KeyCode::Left if self.bearer_cursor > 0 => {
198 self.bearer_cursor -= 1;
199 }
200 KeyCode::Right if self.bearer_cursor < self.bearer_token.len() => {
201 self.bearer_cursor += 1;
202 }
203 _ => {}
204 },
205 Step::PairingCode => match key.code {
206 KeyCode::Enter => {
207 let _ = self.advance_step();
208 }
209 KeyCode::Char(c) => {
210 let pos = self.pairing_cursor.min(self.pairing_code.len());
211 self.pairing_code.insert(pos, c);
212 self.pairing_cursor = pos + 1;
213 }
214 KeyCode::Backspace
215 if self.pairing_cursor > 0
216 && self.pairing_cursor <= self.pairing_code.len() =>
217 {
218 self.pairing_code.remove(self.pairing_cursor - 1);
219 self.pairing_cursor -= 1;
220 }
221 KeyCode::Left if self.pairing_cursor > 0 => {
222 self.pairing_cursor -= 1;
223 }
224 KeyCode::Right if self.pairing_cursor < self.pairing_code.len() => {
225 self.pairing_cursor += 1;
226 }
227 _ => {}
228 },
229 Step::ApiHeader => match key.code {
230 KeyCode::Tab => self.step = Step::ApiKey,
231 KeyCode::Enter => {
232 let _ = self.advance_step();
233 }
234 KeyCode::Char(c) => {
235 let pos = self.header_cursor.min(self.api_header.len());
236 self.api_header.insert(pos, c);
237 self.header_cursor = pos + 1;
238 }
239 KeyCode::Backspace
240 if self.header_cursor > 0 && self.header_cursor <= self.api_header.len() =>
241 {
242 self.api_header.remove(self.header_cursor - 1);
243 self.header_cursor -= 1;
244 }
245 KeyCode::Left if self.header_cursor > 0 => {
246 self.header_cursor -= 1;
247 }
248 KeyCode::Right if self.header_cursor < self.api_header.len() => {
249 self.header_cursor += 1;
250 }
251 _ => {}
252 },
253 Step::ApiKey => match key.code {
254 KeyCode::Tab => self.step = Step::ApiHeader,
255 KeyCode::Enter => {
256 let _ = self.advance_step();
257 }
258 KeyCode::Char(c) => {
259 self.reuse_keyring_api_key = false;
260 let pos = self.api_key_cursor.min(self.api_key.len());
261 self.api_key.insert(pos, c);
262 self.api_key_cursor = pos + 1;
263 }
264 KeyCode::Backspace
265 if self.api_key_cursor > 0 && self.api_key_cursor <= self.api_key.len() =>
266 {
267 self.api_key.remove(self.api_key_cursor - 1);
268 self.api_key_cursor -= 1;
269 }
270 KeyCode::Left if self.api_key_cursor > 0 => {
271 self.api_key_cursor -= 1;
272 }
273 KeyCode::Right if self.api_key_cursor < self.api_key.len() => {
274 self.api_key_cursor += 1;
275 }
276 _ => {}
277 },
278 Step::Summary => {
279 if key.code == KeyCode::Enter {
280 self.testing = true;
281 self.error = None;
282 }
285 }
286 }
287 Ok(false)
288 }
289
290 pub fn handle_paste(&mut self, text: &str) {
291 let clean_text = text.replace(['\n', '\r'], "");
293 if clean_text.is_empty() {
294 return;
295 }
296
297 match self.step {
298 Step::Url => {
299 let pos = self.url_cursor.min(self.url.len());
300 self.url.insert_str(pos, &clean_text);
301 self.url_cursor += clean_text.len();
302 }
303 Step::BasicUser => {
304 let pos = self.user_cursor.min(self.username.len());
305 self.username.insert_str(pos, &clean_text);
306 self.user_cursor += clean_text.len();
307 }
308 Step::BasicPass => {
309 self.reuse_keyring_password = false;
310 self.password.push_str(&clean_text);
311 }
312 Step::Bearer => {
313 self.reuse_keyring_bearer = false;
314 let pos = self.bearer_cursor.min(self.bearer_token.len());
315 self.bearer_token.insert_str(pos, &clean_text);
316 self.bearer_cursor += clean_text.len();
317 }
318 Step::PairingCode => {
319 let pos = self.pairing_cursor.min(self.pairing_code.len());
320 self.pairing_code.insert_str(pos, &clean_text);
321 self.pairing_cursor += clean_text.len();
322 }
323 Step::ApiHeader => {
324 let pos = self.header_cursor.min(self.api_header.len());
325 self.api_header.insert_str(pos, &clean_text);
326 self.header_cursor += clean_text.len();
327 }
328 Step::ApiKey => {
329 self.reuse_keyring_api_key = false;
330 let pos = self.api_key_cursor.min(self.api_key.len());
331 self.api_key.insert_str(pos, &clean_text);
332 self.api_key_cursor += clean_text.len();
333 }
334 _ => {}
335 }
336 }
337}