cli_ui/prompt/core/
runner.rs1use super::super::error::{PromptError, Result};
8use super::{Frame, Prompt, RenderCtx, Step};
9
10pub fn run<P: Prompt>(p: P) -> Result<P::Output> {
15 if super::super::engine::is_interactive() {
16 run_interactive(p)
17 } else {
18 p.run_fallback()
19 }
20}
21
22fn run_interactive<P: Prompt>(mut p: P) -> Result<P::Output> {
23 use super::super::engine::interactive::{enter_raw, leave_raw, read_key};
24 use super::super::render::{cleanup, clear_lines, hide_cursor, show_cursor};
25
26 let mut out = std::io::stderr();
27 let mut error: Option<String> = None;
28
29 enter_raw()?;
30 hide_cursor(&mut out).ok();
31
32 let mut frame = p.render(RenderCtx {
33 error: error.as_deref(),
34 });
35 write_frame(&mut out, &frame);
36
37 let outcome: Result<P::Output> = loop {
38 let key = match read_key() {
39 Ok(k) => k,
40 Err(_) => {
41 cleanup(&mut out, frame.len());
42 return Err(PromptError::Interrupted);
43 }
44 };
45 match p.handle(key) {
46 Step::Continue => {
47 error = None;
48 }
49 Step::Reject(msg) => {
50 error = Some(msg);
51 }
52 Step::Submit(value) => break Ok(value),
53 Step::Cancel => {
54 cleanup(&mut out, frame.len());
55 return Err(PromptError::Interrupted);
56 }
57 }
58 clear_lines(&mut out, frame.len()).ok();
59 frame = p.render(RenderCtx {
60 error: error.as_deref(),
61 });
62 write_frame(&mut out, &frame);
63 };
64
65 if error.is_some() {
69 clear_lines(&mut out, frame.len()).ok();
70 frame = p.render(RenderCtx { error: None });
71 write_frame(&mut out, &frame);
72 }
73
74 show_cursor(&mut out).ok();
75 leave_raw()?;
76
77 let value = outcome?;
78 clear_lines(&mut out, frame.len()).ok();
79 write_answered(&p.render_answered(&value));
80 Ok(value)
81}
82
83fn write_frame(out: &mut std::io::Stderr, frame: &Frame) {
84 use std::io::Write;
85 for line in frame {
86 let _ = out.write_all(line.as_bytes());
87 let _ = out.write_all(b"\r\n");
88 }
89 let _ = out.flush();
90}
91
92fn write_answered(frame: &Frame) {
93 for line in frame {
95 eprintln!("{line}");
96 }
97}