1use super::input::{PlaceholderHighlighter, ValidateFn};
4use crate::{
5 error::ClackError,
6 outro,
7 style::{ansi, chars},
8};
9use crossterm::{QueueableCommand, cursor};
10use owo_colors::OwoColorize;
11use rustyline::Editor;
12use std::{
13 borrow::Cow,
14 error::Error,
15 fmt::Display,
16 io::{Write, stdout},
17 str::FromStr,
18};
19
20pub struct MultiInput<M: Display> {
43 message: M,
44 initial_value: Option<String>,
45 placeholder: Option<String>,
46 validate: Option<Box<ValidateFn>>,
47 cancel: Option<Box<dyn Fn()>>,
48 min: u16,
49 max: u16,
50}
51
52impl<M: Display> MultiInput<M> {
53 pub fn new(message: M) -> Self {
67 MultiInput {
68 message,
69 validate: None,
70 initial_value: None,
71 placeholder: None,
72 cancel: None,
73 min: 1,
74 max: u16::MAX,
75 }
76 }
77
78 pub fn initial_value<S: ToString>(&mut self, initial_value: S) -> &mut Self {
94 self.initial_value = Some(initial_value.to_string());
95 self
96 }
97
98 pub fn placeholder<S: ToString>(&mut self, placeholder: S) -> &mut Self {
114 self.placeholder = Some(placeholder.to_string());
115 self
116 }
117
118 pub fn min(&mut self, min: u16) -> &mut Self {
130 self.min = min;
131 self
132 }
133
134 pub fn max(&mut self, max: u16) -> &mut Self {
149 self.max = max;
150 self
151 }
152
153 pub fn validate<F>(&mut self, validate: F) -> &mut Self
179 where
180 F: Fn(&str) -> Result<(), Cow<'static, str>> + 'static,
181 {
182 let validate = Box::new(validate);
183 self.validate = Some(validate);
184 self
185 }
186
187 fn do_validate(&self, input: &str) -> Result<(), Cow<'static, str>> {
188 if let Some(validate) = self.validate.as_deref() {
189 validate(input)
190 } else {
191 Ok(())
192 }
193 }
194
195 pub fn cancel<F>(&mut self, cancel: F) -> &mut Self
213 where
214 F: Fn() + 'static,
215 {
216 let cancel = Box::new(cancel);
217 self.cancel = Some(cancel);
218 self
219 }
220
221 fn interact_once<T: FromStr>(
222 &self,
223 enforce_non_empty: bool,
224 amt: u16,
225 ) -> Result<Option<T>, ClackError>
226 where
227 T::Err: Error,
228 {
229 let prompt = format!("{} ", *chars::BAR);
230 let mut editor = Editor::new()?;
231
232 let highlighter = PlaceholderHighlighter::new(self.placeholder.as_deref());
233 editor.set_helper(Some(highlighter));
234
235 let mut initial_value = self.initial_value.as_deref().map(Cow::Borrowed);
236 loop {
237 let line = if let Some(ref init) = initial_value {
238 editor.readline_with_initial(&prompt, (init, ""))
239 } else {
240 editor.readline(&prompt)
241 };
242
243 if let Ok(value) = line {
245 if value.is_empty() {
246 if enforce_non_empty {
247 initial_value = None;
248
249 if let Some(helper) = editor.helper_mut() {
250 helper.is_val = true;
251 }
252
253 let text = format!("minimum {}", self.min);
254 self.w_val(&text, amt);
255 } else {
256 break Ok(None);
257 }
258 } else if let Err(text) = self.do_validate(&value) {
259 initial_value = Some(Cow::Owned(value));
260
261 if let Some(helper) = editor.helper_mut() {
262 helper.is_val = true;
263 }
264
265 self.w_val(&text, amt);
266 } else {
267 match value.parse::<T>() {
268 Ok(value) => break Ok(Some(value)),
269 Err(err) => {
270 initial_value = Some(Cow::Owned(value));
271
272 if let Some(helper) = editor.helper_mut() {
273 helper.is_val = true;
274 }
275
276 self.w_val(&err.to_string(), amt);
277 }
278 }
279 }
281 } else {
282 break Err(ClackError::Cancelled);
283 }
284 }
285 }
286
287 pub fn parse<T: FromStr + Display>(&self) -> Result<Vec<T>, ClackError>
302 where
303 T::Err: Error,
304 {
305 self.w_init();
306
307 let mut v = vec![];
308 loop {
309 let amt = v.len() as u16;
310
311 let enforce_non_empty = amt < self.min;
312 let once = self.interact_once::<T>(enforce_non_empty, amt);
313
314 match once {
315 Ok(Some(value)) => {
316 self.w_line(&value, amt);
317 v.push(value);
318
319 if v.len() as u16 == self.max {
320 println!();
321 self.w_out(&v);
322 break;
323 }
324 }
325 Ok(None) => {
326 self.w_out(&v);
327 break;
328 }
329 Err(ClackError::Cancelled) => {
330 self.w_cancel(v.len());
331 if let Some(cancel) = self.cancel.as_deref() {
332 cancel();
333 } else {
334 outro!();
335 }
336
337 return Err(ClackError::Cancelled);
338 }
339 Err(err) => return Err(err),
340 }
341 }
342
343 Ok(v)
344 }
345
346 pub fn interact(&self) -> Result<Vec<String>, ClackError> {
371 self.w_init();
372
373 let mut v = vec![];
374 loop {
375 let amt = v.len() as u16;
376
377 let enforce_non_empty = amt < self.min;
378 let once = self.interact_once::<String>(enforce_non_empty, amt);
379
380 match once {
381 Ok(Some(value)) => {
382 self.w_line(&value, amt);
383 v.push(value);
384
385 if v.len() as u16 == self.max {
386 println!();
387 self.w_out(&v);
388 break;
389 }
390 }
391 Ok(None) => {
392 self.w_out(&v);
393 break;
394 }
395 Err(ClackError::Cancelled) => {
396 self.w_cancel(v.len());
397 if let Some(cancel) = self.cancel.as_deref() {
398 cancel();
399 } else {
400 outro!();
401 }
402
403 return Err(ClackError::Cancelled);
404 }
405 Err(err) => return Err(err),
406 }
407 }
408
409 Ok(v)
410 }
411}
412
413impl<M: Display> MultiInput<M> {
414 fn w_init(&self) {
415 let mut stdout = stdout();
416
417 println!("{}", *chars::BAR);
418 println!("{} {}", (*chars::STEP_ACTIVE).cyan(), self.message);
419 println!("{}", (*chars::BAR).cyan());
420 print!("{}", (*chars::BAR_END).cyan());
421
422 let _ = stdout.queue(cursor::MoveToPreviousLine(1));
423 let _ = stdout.flush();
424
425 print!("{} ", (*chars::BAR).cyan());
426 let _ = stdout.flush();
427 }
428
429 fn w_line<V: Display>(&self, value: V, amt: u16) {
430 let mut stdout = stdout();
431 let _ = stdout.queue(cursor::MoveToPreviousLine(amt + 2));
432 let _ = stdout.flush();
433
434 println!("{} {}", (*chars::STEP_ACTIVE).cyan(), self.message);
435
436 for _ in 0..amt {
437 println!("{}", (*chars::BAR).cyan());
438 }
439
440 println!("{} {}", (*chars::BAR).cyan(), value.dimmed());
441 println!("{}", (*chars::BAR).cyan());
442
443 print!("{}", ansi::CLEAR_LINE);
444 print!("{}", (*chars::BAR_END).cyan());
445
446 let _ = stdout.queue(cursor::MoveToPreviousLine(1));
447 let _ = stdout.flush();
448 }
449
450 fn w_val(&self, text: &str, amt: u16) {
451 let mut stdout = stdout();
452 let _ = stdout.queue(cursor::MoveToPreviousLine(amt + 2));
453 let _ = stdout.flush();
454
455 println!("{} {}", (*chars::STEP_ERROR).yellow(), self.message);
456
457 for _ in 0..=amt {
458 println!("{}", (*chars::BAR).yellow());
459 }
460
461 print!("{}", ansi::CLEAR_LINE);
462 print!("{} {}", (*chars::BAR_END).yellow(), text.yellow());
463
464 let _ = stdout.queue(cursor::MoveToPreviousLine(1));
465 let _ = stdout.flush();
466 }
467
468 fn w_out<V: Display>(&self, values: &[V]) {
469 let amt = values.len();
470
471 let mut stdout = stdout();
472 let _ = stdout.queue(cursor::MoveToPreviousLine(amt as u16 + 2));
473 let _ = stdout.flush();
474
475 println!("{} {}", (*chars::STEP_SUBMIT).green(), self.message);
476
477 if amt == 0 {
478 println!("{}", *chars::BAR);
479 }
480
481 for val in values {
482 println!("{} {}", *chars::BAR, val.dimmed());
483 }
484
485 println!("{}", ansi::CLEAR_LINE);
486 println!("{}", ansi::CLEAR_LINE);
487
488 let _ = stdout.queue(cursor::MoveToPreviousLine(2));
489 let _ = stdout.flush();
490 }
491
492 fn w_cancel(&self, amt: usize) {
493 let mut stdout = stdout();
494 let _ = stdout.queue(cursor::MoveToPreviousLine(1));
495 let _ = stdout.flush();
496
497 print!("{}", ansi::CLEAR_LINE);
498 println!("{} {}", *chars::BAR, "cancelled".strikethrough().dimmed());
499
500 print!("{}", ansi::CLEAR_LINE);
501
502 let _ = stdout.queue(cursor::MoveToPreviousLine(amt as u16 + 2));
503 let _ = stdout.flush();
504
505 println!("{} {}", (*chars::STEP_CANCEL).red(), self.message);
506
507 for _ in 0..amt {
508 println!("{}", *chars::BAR);
509 }
510
511 let _ = stdout.queue(cursor::MoveToNextLine(1));
512 let _ = stdout.flush();
513 }
514}
515
516pub fn multi_input<M: Display>(message: M) -> MultiInput<M> {
518 MultiInput::new(message)
519}