use std::io::Write;
use crossterm::{
cursor,
event::{Event, KeyCode, KeyModifiers},
queue, style, terminal,
};
use xacli_core::{ComponentInfo, Context, Error, InputComponent, InputValue, Result};
pub struct Confirm {
prompt: String,
default: bool,
}
impl Confirm {
pub fn new(prompt: impl Into<String>) -> Self {
Self {
prompt: prompt.into(),
default: false,
}
}
fn render(&self, ctx: &mut dyn Context, choice: bool) -> Result<()> {
let hint = if choice { "(Y/n)" } else { "(y/N)" };
let mut stdout = ctx.stdout();
queue!(
stdout,
cursor::MoveToColumn(0),
terminal::Clear(terminal::ClearType::CurrentLine),
style::Print(&self.prompt),
style::Print(" "),
style::Print(hint),
)?;
stdout.flush()?;
Ok(())
}
fn run_inner(&mut self, ctx: &mut dyn Context) -> Result<InputValue> {
let mut choice = self.default;
self.render(ctx, choice)?;
loop {
if let Event::Key(key) = ctx.read_event()? {
match key.code {
KeyCode::Enter => {
return Ok(InputValue::Bool(choice));
}
KeyCode::Esc => {
return Err(Error::InterruptError);
}
KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
return Err(Error::InterruptError);
}
KeyCode::Char('y') | KeyCode::Char('Y') => {
choice = true;
self.render(ctx, choice)?;
}
KeyCode::Char('n') | KeyCode::Char('N') => {
choice = false;
self.render(ctx, choice)?;
}
KeyCode::Left | KeyCode::Right => {
choice = !choice;
self.render(ctx, choice)?;
}
_ => {}
}
}
}
}
}
impl InputComponent for Confirm {
fn info(&self) -> ComponentInfo {
ComponentInfo {
name: "confirm".to_string(),
title: "Confirmation Prompt".to_string(),
description: self.prompt.clone(),
}
}
fn run(&mut self, ctx: &mut dyn Context) -> Result<InputValue> {
terminal::enable_raw_mode()?;
let result = self.run_inner(ctx);
let _ = terminal::disable_raw_mode();
result
}
}