1use std::{collections::BTreeSet, fmt::Display, io::Result};
2
3use unicode_width::UnicodeWidthStr;
4
5use crate::{
6 error::InquireResult,
7 input::Input,
8 list_option::ListOption,
9 terminal::Terminal,
10 ui::{IndexPrefix, Key, RenderConfig, Styled},
11 utils::{int_log10, Page},
12 validator::ErrorMessage,
13};
14
15use super::{frame_renderer::FrameRenderer, InputReader};
16
17pub trait CommonBackend: InputReader {
18 fn frame_setup(&mut self) -> Result<()>;
19 fn frame_finish(&mut self, is_last_frame: bool) -> Result<()>;
20
21 fn render_canceled_prompt(&mut self, prompt: &str) -> Result<()>;
22 fn render_prompt_with_answer(&mut self, prompt: &str, answer: &str) -> Result<()>;
23
24 fn render_error_message(&mut self, error: &ErrorMessage) -> Result<()>;
25 fn render_help_message(&mut self, help: &str) -> Result<()>;
26}
27
28pub trait TextBackend: CommonBackend {
29 fn render_prompt(
30 &mut self,
31 prompt: &str,
32 default: Option<&str>,
33 cur_input: &Input,
34 ) -> Result<()>;
35 fn render_suggestions<D: Display>(&mut self, page: Page<'_, ListOption<D>>) -> Result<()>;
36}
37
38#[cfg(feature = "editor")]
39pub trait EditorBackend: CommonBackend {
40 fn render_prompt(&mut self, prompt: &str, editor_command: &str) -> Result<()>;
41}
42
43pub trait SelectBackend: CommonBackend {
44 fn render_select_prompt(&mut self, prompt: &str, cur_input: Option<&Input>) -> Result<()>;
45 fn render_options<D: Display>(&mut self, page: Page<'_, ListOption<D>>) -> Result<()>;
46}
47
48pub trait MultiSelectBackend: CommonBackend {
49 fn render_multiselect_prompt(&mut self, prompt: &str, cur_input: Option<&Input>) -> Result<()>;
50 fn render_options<D: Display>(
51 &mut self,
52 page: Page<'_, ListOption<D>>,
53 checked: &BTreeSet<usize>,
54 ) -> Result<()>;
55}
56
57pub trait CustomTypeBackend: CommonBackend {
58 fn render_prompt(
59 &mut self,
60 prompt: &str,
61 default: Option<&str>,
62 cur_input: &Input,
63 ) -> Result<()>;
64}
65
66pub trait PasswordBackend: CommonBackend {
67 fn render_prompt(&mut self, prompt: &str) -> Result<()>;
68 fn render_prompt_with_masked_input(&mut self, prompt: &str, cur_input: &Input) -> Result<()>;
69 fn render_prompt_with_full_input(&mut self, prompt: &str, cur_input: &Input) -> Result<()>;
70}
71
72#[derive(Clone, Copy, Debug, Default)]
73pub struct Position {
74 pub row: u16,
75 pub col: u16,
76}
77
78pub struct Backend<'a, I, T>
79where
80 I: InputReader,
81 T: Terminal,
82{
83 frame_renderer: FrameRenderer<T>,
84 input_reader: I,
85 render_config: RenderConfig<'a>,
86}
87
88impl<'a, I, T> Backend<'a, I, T>
89where
90 I: InputReader,
91 T: Terminal,
92{
93 #[allow(clippy::large_types_passed_by_value)]
94 pub fn new(input_reader: I, terminal: T, render_config: RenderConfig<'a>) -> Result<Self> {
95 let backend = Self {
96 frame_renderer: FrameRenderer::new(terminal)?,
97 input_reader,
98 render_config,
99 };
100
101 Ok(backend)
102 }
103
104 fn print_option_prefix<D: Display>(
105 &mut self,
106 option_relative_index: usize,
107 page: &Page<'_, ListOption<D>>,
108 ) -> Result<()> {
109 let empty_prefix = Styled::new(" ");
110
111 let x = if page.cursor == Some(option_relative_index) {
112 self.render_config.highlighted_option_prefix
113 } else if option_relative_index == 0 && !page.first {
114 self.render_config.scroll_up_prefix
115 } else if (option_relative_index + 1) == page.content.len() && !page.last {
116 self.render_config.scroll_down_prefix
117 } else {
118 empty_prefix
119 };
120
121 self.frame_renderer.write_styled(x)
122 }
123
124 fn print_option_value<D: Display>(
125 &mut self,
126 option_relative_index: usize,
127 option: &ListOption<D>,
128 page: &Page<'_, ListOption<D>>,
129 ) -> Result<()> {
130 let stylesheet = if let Some(selected_option_style) = self.render_config.selected_option {
131 match page.cursor {
132 Some(cursor) if cursor == option_relative_index => selected_option_style,
133 _ => self.render_config.option,
134 }
135 } else {
136 self.render_config.option
137 };
138
139 self.frame_renderer
140 .write_styled(Styled::new(&option.value).with_style_sheet(stylesheet))
141 }
142
143 fn print_option_index_prefix(&mut self, index: usize, max_index: usize) -> Option<Result<()>> {
144 let index = index.saturating_add(1);
145
146 let content = match self.render_config.option_index_prefix {
147 IndexPrefix::None => None,
148 IndexPrefix::Simple => Some(format!("{index})")),
149 IndexPrefix::SpacePadded => {
150 let width = int_log10(max_index.saturating_add(1));
151 Some(format!("{index:width$})"))
152 }
153 IndexPrefix::ZeroPadded => {
154 let width = int_log10(max_index.saturating_add(1));
155 Some(format!("{index:0width$})"))
156 }
157 };
158
159 content.map(|prefix| {
160 self.frame_renderer
161 .write_styled(Styled::new(prefix).with_style_sheet(self.render_config.option))
162 })
163 }
164
165 fn print_default_value(&mut self, value: &str) -> Result<()> {
166 let content = format!("({value})");
167 let token = Styled::new(content).with_style_sheet(self.render_config.default_value);
168
169 self.frame_renderer.write_styled(token)
170 }
171
172 fn print_prompt_with_prefix(&mut self, prefix: Styled<&str>, prompt: &str) -> Result<()> {
173 self.frame_renderer.write_styled(prefix)?;
174
175 self.frame_renderer.write(" ")?;
176
177 self.frame_renderer
178 .write_styled(Styled::new(prompt).with_style_sheet(self.render_config.prompt))?;
179
180 Ok(())
181 }
182
183 fn print_prompt(&mut self, prompt: &str) -> Result<()> {
184 self.print_prompt_with_prefix(self.render_config.prompt_prefix, prompt)
185 }
186
187 fn print_input(&mut self, input: &Input) -> Result<()> {
188 self.frame_renderer.write(" ")?;
189
190 self.frame_renderer
195 .mark_cursor_position(input.pre_cursor().width() as isize);
196
197 if input.is_empty() {
198 match input.placeholder() {
199 Some("") | None => {}
200 Some(p) => self.frame_renderer.write_styled(
201 Styled::new(p).with_style_sheet(self.render_config.placeholder),
202 )?,
203 }
204 } else {
205 self.frame_renderer.write_styled(
206 Styled::new(input.content()).with_style_sheet(self.render_config.text_input),
207 )?;
208 }
209
210 if input.cursor() == input.length() {
214 self.frame_renderer.write(' ')?;
215 }
216
217 Ok(())
218 }
219
220 fn print_prompt_with_input(
221 &mut self,
222 prompt: &str,
223 default: Option<&str>,
224 input: &Input,
225 ) -> Result<()> {
226 self.print_prompt(prompt)?;
227
228 if let Some(default) = default {
229 self.frame_renderer.write(" ")?;
230 self.print_default_value(default)?;
231 }
232
233 self.print_input(input)?;
234
235 self.new_line()?;
236
237 Ok(())
238 }
239
240 fn new_line(&mut self) -> Result<()> {
241 self.frame_renderer.write("\n")?;
242 Ok(())
243 }
244}
245
246impl<'a, I, T> CommonBackend for Backend<'a, I, T>
247where
248 I: InputReader,
249 T: Terminal,
250{
251 fn frame_setup(&mut self) -> Result<()> {
252 self.frame_renderer.start_frame()
253 }
254
255 fn frame_finish(&mut self, is_last_frame: bool) -> Result<()> {
256 self.frame_renderer.finish_current_frame(is_last_frame)
257 }
258
259 fn render_canceled_prompt(&mut self, prompt: &str) -> Result<()> {
260 self.print_prompt(prompt)?;
261
262 self.frame_renderer.write(" ")?;
263
264 self.frame_renderer
265 .write_styled(self.render_config.canceled_prompt_indicator)?;
266
267 self.new_line()?;
268
269 Ok(())
270 }
271
272 fn render_prompt_with_answer(&mut self, prompt: &str, answer: &str) -> Result<()> {
273 self.print_prompt_with_prefix(self.render_config.answered_prompt_prefix, prompt)?;
274
275 self.frame_renderer.write(" ")?;
276
277 let token = Styled::new(answer).with_style_sheet(self.render_config.answer);
278 self.frame_renderer.write_styled(token)?;
279
280 self.new_line()?;
281
282 Ok(())
283 }
284
285 fn render_error_message(&mut self, error: &ErrorMessage) -> Result<()> {
286 self.frame_renderer
287 .write_styled(self.render_config.error_message.prefix)?;
288
289 self.frame_renderer.write_styled(
290 Styled::new(" ").with_style_sheet(self.render_config.error_message.separator),
291 )?;
292
293 let message = match error {
294 ErrorMessage::Default => self.render_config.error_message.default_message,
295 ErrorMessage::Custom(msg) => msg,
296 };
297
298 self.frame_renderer.write_styled(
299 Styled::new(message).with_style_sheet(self.render_config.error_message.message),
300 )?;
301
302 self.new_line()?;
303
304 Ok(())
305 }
306
307 fn render_help_message(&mut self, help: &str) -> Result<()> {
308 self.new_line()?;
309
310 self.frame_renderer
311 .write_styled(Styled::new(help).with_style_sheet(self.render_config.help_message))?;
312
313 self.new_line()?;
314
315 Ok(())
316 }
317}
318
319impl<'a, I, T> TextBackend for Backend<'a, I, T>
320where
321 I: InputReader,
322 T: Terminal,
323{
324 fn render_prompt(
325 &mut self,
326 prompt: &str,
327 default: Option<&str>,
328 cur_input: &Input,
329 ) -> Result<()> {
330 self.print_prompt_with_input(prompt, default, cur_input)
331 }
332
333 fn render_suggestions<D: Display>(&mut self, page: Page<'_, ListOption<D>>) -> Result<()> {
334 for (idx, option) in page.content.iter().enumerate() {
335 self.print_option_prefix(idx, &page)?;
336
337 self.frame_renderer.write(" ")?;
338 self.print_option_value(idx, option, &page)?;
339
340 self.new_line()?;
341 }
342
343 Ok(())
344 }
345}
346
347#[cfg(feature = "editor")]
348impl<'a, I, T> EditorBackend for Backend<'a, I, T>
349where
350 I: InputReader,
351 T: Terminal,
352{
353 fn render_prompt(&mut self, prompt: &str, editor_command: &str) -> Result<()> {
354 self.print_prompt(prompt)?;
355
356 self.frame_renderer.write(" ")?;
357
358 let message = format!("[(e) to open {}, (enter) to submit]", editor_command);
359 let token = Styled::new(message).with_style_sheet(self.render_config.editor_prompt);
360 self.frame_renderer.write_styled(token)?;
361
362 self.new_line()?;
363
364 Ok(())
365 }
366}
367
368impl<'a, I, T> SelectBackend for Backend<'a, I, T>
369where
370 I: InputReader,
371 T: Terminal,
372{
373 fn render_select_prompt(&mut self, prompt: &str, cur_input: Option<&Input>) -> Result<()> {
374 if let Some(input) = cur_input {
375 self.print_prompt_with_input(prompt, None, input)
376 } else {
377 self.print_prompt(prompt)
378 }
379 }
380
381 fn render_options<D: Display>(&mut self, page: Page<'_, ListOption<D>>) -> Result<()> {
382 for (idx, option) in page.content.iter().enumerate() {
383 self.print_option_prefix(idx, &page)?;
384
385 self.frame_renderer.write(" ")?;
386
387 if let Some(res) = self.print_option_index_prefix(option.index, page.total) {
388 res?;
389 self.frame_renderer.write(" ")?;
390 }
391
392 self.print_option_value(idx, option, &page)?;
393
394 self.new_line()?;
395 }
396
397 Ok(())
398 }
399}
400
401impl<'a, I, T> MultiSelectBackend for Backend<'a, I, T>
402where
403 I: InputReader,
404 T: Terminal,
405{
406 fn render_multiselect_prompt(&mut self, prompt: &str, cur_input: Option<&Input>) -> Result<()> {
407 if let Some(input) = cur_input {
408 self.print_prompt_with_input(prompt, None, input)
409 } else {
410 self.print_prompt(prompt)
411 }
412 }
413
414 fn render_options<D: Display>(
415 &mut self,
416 page: Page<'_, ListOption<D>>,
417 checked: &BTreeSet<usize>,
418 ) -> Result<()> {
419 for (idx, option) in page.content.iter().enumerate() {
420 self.print_option_prefix(idx, &page)?;
421
422 self.frame_renderer.write(" ")?;
423
424 if let Some(res) = self.print_option_index_prefix(option.index, page.total) {
425 res?;
426 self.frame_renderer.write(" ")?;
427 }
428
429 let mut checkbox = match checked.contains(&option.index) {
430 true => self.render_config.selected_checkbox,
431 false => self.render_config.unselected_checkbox,
432 };
433
434 match (self.render_config.selected_option, page.cursor) {
435 (Some(stylesheet), Some(cursor)) if cursor == idx => checkbox.style = stylesheet,
436 _ => {}
437 }
438
439 self.frame_renderer.write_styled(checkbox)?;
440
441 self.frame_renderer.write(" ")?;
442
443 self.print_option_value(idx, option, &page)?;
444
445 self.new_line()?;
446 }
447
448 Ok(())
449 }
450}
451
452#[cfg(feature = "date")]
453pub mod date {
454 use std::{io::Result, ops::Sub};
455
456 use chrono::{Datelike, Duration};
457
458 use crate::{
459 date_utils::get_start_date,
460 terminal::Terminal,
461 ui::{InputReader, Styled},
462 };
463
464 use super::{Backend, CommonBackend};
465
466 pub trait DateSelectBackend: CommonBackend {
467 fn render_calendar_prompt(&mut self, prompt: &str) -> Result<()>;
468
469 #[allow(clippy::too_many_arguments)]
470 fn render_calendar(
471 &mut self,
472 month: chrono::Month,
473 year: i32,
474 week_start: chrono::Weekday,
475 today: chrono::NaiveDate,
476 selected_date: chrono::NaiveDate,
477 min_date: Option<chrono::NaiveDate>,
478 max_date: Option<chrono::NaiveDate>,
479 ) -> Result<()>;
480 }
481
482 impl<'a, I, T> DateSelectBackend for Backend<'a, I, T>
483 where
484 I: InputReader,
485 T: Terminal,
486 {
487 fn render_calendar_prompt(&mut self, prompt: &str) -> Result<()> {
488 self.print_prompt(prompt)?;
489 self.new_line()?;
490 Ok(())
491 }
492
493 fn render_calendar(
494 &mut self,
495 month: chrono::Month,
496 year: i32,
497 week_start: chrono::Weekday,
498 today: chrono::NaiveDate,
499 selected_date: chrono::NaiveDate,
500 min_date: Option<chrono::NaiveDate>,
501 max_date: Option<chrono::NaiveDate>,
502 ) -> Result<()> {
503 macro_rules! write_prefix {
504 () => {{
505 self.frame_renderer
506 .write_styled(self.render_config.calendar.prefix)?;
507 self.frame_renderer.write(" ")
508 }};
509 }
510
511 let header = format!("{} {}", month.name().to_lowercase(), year);
513 let header = format!("{header:^20}");
514 let header = Styled::new(header).with_style_sheet(self.render_config.calendar.header);
515
516 write_prefix!()?;
517
518 self.frame_renderer.write_styled(header)?;
519
520 self.new_line()?;
521
522 let mut current_weekday = week_start;
524 let mut week_days: Vec<String> = vec![];
525 for _ in 0..7 {
526 let mut formatted = format!("{current_weekday}");
527 formatted.make_ascii_lowercase();
528 formatted.pop();
529 week_days.push(formatted);
530
531 current_weekday = current_weekday.succ();
532 }
533
534 let week_days = Styled::new(week_days.join(" "))
535 .with_style_sheet(self.render_config.calendar.week_header);
536
537 write_prefix!()?;
538
539 self.frame_renderer.write_styled(week_days)?;
540 self.new_line()?;
541
542 let mut date_it = get_start_date(month, year);
544 if date_it.weekday() == week_start {
546 date_it = date_it.sub(
547 Duration::try_weeks(1).expect("overflow when calculating duration of 1 week"),
548 );
549 } else {
550 while date_it.weekday() != week_start {
551 date_it = match date_it.pred_opt() {
552 Some(date) => date,
553 None => break,
554 };
555 }
556 }
557
558 for _ in 0..6 {
559 write_prefix!()?;
560
561 for i in 0..7 {
562 if i > 0 {
563 self.frame_renderer.write(" ")?;
564 }
565
566 let date = format!("{:2}", date_it.day());
567
568 let cursor_offset = if date_it.day() < 10 { 1 } else { 0 };
569
570 let mut style_sheet = crate::ui::StyleSheet::empty();
571
572 if date_it == selected_date {
573 self.frame_renderer.mark_cursor_position(cursor_offset);
574 if let Some(custom_style_sheet) = self.render_config.calendar.selected_date
575 {
576 style_sheet = custom_style_sheet;
577 }
578 } else if date_it == today {
579 style_sheet = self.render_config.calendar.today_date;
580 } else if date_it.month() != month.number_from_month() {
581 style_sheet = self.render_config.calendar.different_month_date;
582 }
583
584 if let Some(min_date) = min_date {
585 if date_it < min_date {
586 style_sheet = self.render_config.calendar.unavailable_date;
587 }
588 }
589
590 if let Some(max_date) = max_date {
591 if date_it > max_date {
592 style_sheet = self.render_config.calendar.unavailable_date;
593 }
594 }
595
596 let token = Styled::new(date).with_style_sheet(style_sheet);
597 self.frame_renderer.write_styled(token)?;
598
599 date_it = date_it.succ_opt().unwrap_or(date_it);
600 }
601
602 self.new_line()?;
603 }
604
605 Ok(())
606 }
607 }
608}
609
610impl<'a, I, T> CustomTypeBackend for Backend<'a, I, T>
611where
612 I: InputReader,
613 T: Terminal,
614{
615 fn render_prompt(
616 &mut self,
617 prompt: &str,
618 default: Option<&str>,
619 cur_input: &Input,
620 ) -> Result<()> {
621 self.print_prompt_with_input(prompt, default, cur_input)
622 }
623}
624
625impl<'a, I, T> PasswordBackend for Backend<'a, I, T>
626where
627 I: InputReader,
628 T: Terminal,
629{
630 fn render_prompt(&mut self, prompt: &str) -> Result<()> {
631 self.print_prompt(prompt)?;
632 self.new_line()?;
633 Ok(())
634 }
635
636 fn render_prompt_with_masked_input(&mut self, prompt: &str, cur_input: &Input) -> Result<()> {
637 let masked_string: String = (0..cur_input.length())
638 .map(|_| self.render_config.password_mask)
639 .collect();
640
641 let masked_input = Input::new_with(masked_string).with_cursor(cur_input.cursor());
642
643 self.print_prompt_with_input(prompt, None, &masked_input)
644 }
645
646 fn render_prompt_with_full_input(&mut self, prompt: &str, cur_input: &Input) -> Result<()> {
647 self.print_prompt_with_input(prompt, None, cur_input)
648 }
649}
650
651impl<'a, I, T> InputReader for Backend<'a, I, T>
652where
653 I: InputReader,
654 T: Terminal,
655{
656 fn read_key(&mut self) -> InquireResult<Key> {
657 self.input_reader.read_key()
658 }
659}
660
661#[cfg(test)]
662pub(crate) mod test {
663 use std::collections::VecDeque;
664
665 use chrono::{Month, NaiveDate, Weekday};
666
667 use crate::{
668 input::Input,
669 ui::{InputReader, Key},
670 validator::ErrorMessage,
671 };
672
673 use super::{CommonBackend, CustomTypeBackend};
674
675 #[derive(Debug, Clone, PartialEq)]
676 pub enum Token {
677 Prompt(String),
678 DefaultValue(String),
679 Input(Input),
680 CanceledPrompt(String),
681 AnsweredPrompt(String, String),
682 ErrorMessage(ErrorMessage),
683 HelpMessage(String),
684 Calendar {
685 month: Month,
686 year: i32,
687 week_start: Weekday,
688 today: NaiveDate,
689 selected_date: NaiveDate,
690 min_date: Option<NaiveDate>,
691 max_date: Option<NaiveDate>,
692 },
693 PromptEnd,
694 }
695
696 #[derive(Default, Debug, Clone)]
697 pub struct Frame {
698 content: Vec<Token>,
699 }
700
701 impl Frame {
702 pub fn has_token(&self, token: &Token) -> bool {
703 self.content.iter().any(|t| t == token)
704 }
705
706 pub fn tokens(&self) -> &[Token] {
707 &self.content
708 }
709 }
710
711 #[derive(Default, Debug, Clone)]
712 pub struct FakeBackend {
713 pub input: VecDeque<Key>,
714 pub frames: Vec<Frame>,
715 pub cur_frame: Option<Frame>,
716 }
717
718 impl FakeBackend {
719 pub fn new(input: Vec<Key>) -> Self {
720 Self {
721 input: input.into(),
722 frames: vec![],
723 cur_frame: None,
724 }
725 }
726
727 fn push_token(&mut self, token: Token) {
728 if let Some(frame) = self.cur_frame.as_mut() {
729 frame.content.push(token);
730 } else {
731 panic!("No frame to push token");
732 }
733 }
734 pub fn frames(&self) -> &[Frame] {
735 &self.frames
736 }
737 }
738
739 impl InputReader for FakeBackend {
740 fn read_key(&mut self) -> crate::error::InquireResult<Key> {
741 self.input
742 .pop_front()
743 .ok_or(crate::error::InquireError::IO(std::io::Error::new(
744 std::io::ErrorKind::UnexpectedEof,
745 "No more keys in input",
746 )))
747 }
748 }
749
750 impl CommonBackend for FakeBackend {
751 fn frame_setup(&mut self) -> std::io::Result<()> {
752 self.cur_frame = Some(Frame::default());
753 Ok(())
754 }
755
756 fn frame_finish(&mut self, is_last_frame: bool) -> std::io::Result<()> {
757 if is_last_frame {
758 self.push_token(Token::PromptEnd);
759 }
760
761 if let Some(frame) = self.cur_frame.take() {
762 self.frames.push(frame);
763 } else {
764 panic!("No frame to finish");
765 }
766 Ok(())
767 }
768
769 fn render_canceled_prompt(&mut self, prompt: &str) -> std::io::Result<()> {
770 self.push_token(Token::CanceledPrompt(prompt.to_string()));
771 Ok(())
772 }
773
774 fn render_prompt_with_answer(&mut self, prompt: &str, answer: &str) -> std::io::Result<()> {
775 self.push_token(Token::AnsweredPrompt(
776 prompt.to_string(),
777 answer.to_string(),
778 ));
779 Ok(())
780 }
781
782 fn render_error_message(&mut self, error: &ErrorMessage) -> std::io::Result<()> {
783 self.push_token(Token::ErrorMessage(error.clone()));
784 Ok(())
785 }
786
787 fn render_help_message(&mut self, help: &str) -> std::io::Result<()> {
788 self.push_token(Token::HelpMessage(help.to_string()));
789 Ok(())
790 }
791 }
792
793 #[cfg(feature = "date")]
794 impl crate::ui::date::DateSelectBackend for FakeBackend {
795 fn render_calendar_prompt(&mut self, prompt: &str) -> std::io::Result<()> {
796 self.push_token(Token::Prompt(prompt.to_string()));
797 Ok(())
798 }
799
800 fn render_calendar(
801 &mut self,
802 month: Month,
803 year: i32,
804 week_start: Weekday,
805 today: NaiveDate,
806 selected_date: NaiveDate,
807 min_date: Option<NaiveDate>,
808 max_date: Option<NaiveDate>,
809 ) -> std::io::Result<()> {
810 self.push_token(Token::Calendar {
811 month,
812 year,
813 week_start,
814 today,
815 selected_date,
816 min_date,
817 max_date,
818 });
819 Ok(())
820 }
821 }
822
823 impl CustomTypeBackend for FakeBackend {
824 fn render_prompt(
825 &mut self,
826 prompt: &str,
827 default: Option<&str>,
828 cur_input: &Input,
829 ) -> std::io::Result<()> {
830 self.push_token(Token::Prompt(prompt.to_string()));
831 if let Some(default) = default {
832 self.push_token(Token::DefaultValue(default.to_string()));
833 }
834 self.push_token(Token::Input(cur_input.clone()));
835 Ok(())
836 }
837 }
838}