xacli_components/components/confirm/
mod.rs

1use std::io::Write;
2
3use crossterm::{
4    cursor,
5    event::{Event, KeyCode, KeyModifiers},
6    queue, style, terminal,
7};
8use xacli_core::{ComponentInfo, Context, Error, InputComponent, InputValue, Result};
9
10pub struct Confirm {
11    prompt: String,
12    default: bool,
13}
14
15impl Confirm {
16    pub fn new(prompt: impl Into<String>) -> Self {
17        Self {
18            prompt: prompt.into(),
19            default: false,
20        }
21    }
22
23    fn render(&self, ctx: &mut dyn Context, choice: bool) -> Result<()> {
24        let hint = if choice { "(Y/n)" } else { "(y/N)" };
25
26        let mut stdout = ctx.stdout();
27
28        queue!(
29            stdout,
30            cursor::MoveToColumn(0),
31            terminal::Clear(terminal::ClearType::CurrentLine),
32            style::Print(&self.prompt),
33            style::Print(" "),
34            style::Print(hint),
35        )?;
36
37        stdout.flush()?;
38        Ok(())
39    }
40
41    fn run_inner(&mut self, ctx: &mut dyn Context) -> Result<InputValue> {
42        let mut choice = self.default;
43
44        self.render(ctx, choice)?;
45
46        loop {
47            if let Event::Key(key) = ctx.read_event()? {
48                match key.code {
49                    KeyCode::Enter => {
50                        return Ok(InputValue::Bool(choice));
51                    }
52                    KeyCode::Esc => {
53                        return Err(Error::InterruptError);
54                    }
55                    KeyCode::Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) => {
56                        return Err(Error::InterruptError);
57                    }
58                    KeyCode::Char('y') | KeyCode::Char('Y') => {
59                        choice = true;
60                        self.render(ctx, choice)?;
61                    }
62                    KeyCode::Char('n') | KeyCode::Char('N') => {
63                        choice = false;
64                        self.render(ctx, choice)?;
65                    }
66                    KeyCode::Left | KeyCode::Right => {
67                        choice = !choice;
68                        self.render(ctx, choice)?;
69                    }
70                    _ => {}
71                }
72            }
73        }
74    }
75}
76
77impl InputComponent for Confirm {
78    fn info(&self) -> ComponentInfo {
79        ComponentInfo {
80            name: "confirm".to_string(),
81            title: "Confirmation Prompt".to_string(),
82            description: self.prompt.clone(),
83        }
84    }
85
86    fn run(&mut self, ctx: &mut dyn Context) -> Result<InputValue> {
87        // Enable raw mode for keyboard input
88        terminal::enable_raw_mode()?;
89        let result = self.run_inner(ctx);
90        // Always disable raw mode when done
91        let _ = terminal::disable_raw_mode();
92        result
93    }
94}