xacli_components/components/input/
mod.rs1use std::io::Write;
2
3use crossterm::{
4 cursor,
5 event::{Event, KeyCode, KeyModifiers},
6 queue, style, terminal,
7};
8use xacli_core::{Context, Error, InputValue, Result};
9
10pub struct Input {
11 prompt: String,
12 default: Option<String>,
13}
14
15impl Input {
16 pub fn new(prompt: impl Into<String>) -> Self {
17 Self {
18 prompt: prompt.into(),
19 default: None,
20 }
21 }
22
23 pub fn run(self, ctx: &mut dyn Context) -> Result<InputValue> {
24 terminal::enable_raw_mode()?;
26 let result = self.run_inner(ctx);
27 let _ = terminal::disable_raw_mode();
29 result
30 }
31
32 fn run_inner(self, ctx: &mut dyn Context) -> Result<InputValue> {
33 let mut input = self.default.clone().unwrap_or_default();
34 let mut cursor_pos = input.len();
35
36 let stdout = &mut ctx.stdout();
37
38 self.render(stdout, &input, cursor_pos)?;
40
41 loop {
42 if let Event::Key(key) = ctx.read_event()? {
43 match key.code {
44 KeyCode::Enter => {
45 return Ok(InputValue::String(input));
46 }
47 KeyCode::Esc => {
48 return Err(Error::InterruptError);
49 }
50 KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
51 return Err(Error::InterruptError);
52 }
53 KeyCode::Char(c) => {
54 input.insert(cursor_pos, c);
55 cursor_pos += 1;
56 }
57 KeyCode::Backspace => {
58 if cursor_pos > 0 {
59 cursor_pos -= 1;
60 input.remove(cursor_pos);
61 }
62 }
63 KeyCode::Delete => {
64 if cursor_pos < input.len() {
65 input.remove(cursor_pos);
66 }
67 }
68 KeyCode::Left => {
69 cursor_pos = cursor_pos.saturating_sub(1);
70 }
71 KeyCode::Right => {
72 if cursor_pos < input.len() {
73 cursor_pos += 1;
74 }
75 }
76 KeyCode::Home => {
77 cursor_pos = 0;
78 }
79 KeyCode::End => {
80 cursor_pos = input.len();
81 }
82 _ => {}
83 }
84
85 self.render(stdout, &input, cursor_pos)?;
86 }
87 }
88 }
89
90 fn render<W: Write>(&self, stdout: &mut W, input: &str, cursor_pos: usize) -> Result<()> {
91 queue!(
92 stdout,
93 cursor::MoveToColumn(0),
94 terminal::Clear(terminal::ClearType::CurrentLine),
95 style::Print(&self.prompt),
96 style::Print(" "),
97 style::Print(input),
98 cursor::MoveToColumn((self.prompt.len() + 1 + cursor_pos) as u16)
99 )?;
100
101 stdout.flush()?;
102 Ok(())
103 }
104}