use std::io::Write;
use crossterm::{
cursor,
event::{Event, KeyCode, KeyModifiers},
queue, style, terminal,
};
use xacli_core::{Context, Error, InputValue, Result};
pub struct Input {
prompt: String,
default: Option<String>,
}
impl Input {
pub fn new(prompt: impl Into<String>) -> Self {
Self {
prompt: prompt.into(),
default: None,
}
}
pub fn run(self, ctx: &mut dyn Context) -> Result<InputValue> {
terminal::enable_raw_mode()?;
let result = self.run_inner(ctx);
let _ = terminal::disable_raw_mode();
result
}
fn run_inner(self, ctx: &mut dyn Context) -> Result<InputValue> {
let mut input = self.default.clone().unwrap_or_default();
let mut cursor_pos = input.len();
let stdout = &mut ctx.stdout();
self.render(stdout, &input, cursor_pos)?;
loop {
if let Event::Key(key) = ctx.read_event()? {
match key.code {
KeyCode::Enter => {
return Ok(InputValue::String(input));
}
KeyCode::Esc => {
return Err(Error::InterruptError);
}
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
return Err(Error::InterruptError);
}
KeyCode::Char(c) => {
input.insert(cursor_pos, c);
cursor_pos += 1;
}
KeyCode::Backspace => {
if cursor_pos > 0 {
cursor_pos -= 1;
input.remove(cursor_pos);
}
}
KeyCode::Delete => {
if cursor_pos < input.len() {
input.remove(cursor_pos);
}
}
KeyCode::Left => {
cursor_pos = cursor_pos.saturating_sub(1);
}
KeyCode::Right => {
if cursor_pos < input.len() {
cursor_pos += 1;
}
}
KeyCode::Home => {
cursor_pos = 0;
}
KeyCode::End => {
cursor_pos = input.len();
}
_ => {}
}
self.render(stdout, &input, cursor_pos)?;
}
}
}
fn render<W: Write>(&self, stdout: &mut W, input: &str, cursor_pos: usize) -> Result<()> {
queue!(
stdout,
cursor::MoveToColumn(0),
terminal::Clear(terminal::ClearType::CurrentLine),
style::Print(&self.prompt),
style::Print(" "),
style::Print(input),
cursor::MoveToColumn((self.prompt.len() + 1 + cursor_pos) as u16)
)?;
stdout.flush()?;
Ok(())
}
}