actr_cli/core/components/
user_interface.rs1use anyhow::{Context, Result};
2use async_trait::async_trait;
3use dialoguer::{Confirm, Select, theme::ColorfulTheme};
4use std::io::{self, Write};
5
6use crate::core::{ActrCliError, ProgressBar, ServiceInfo, UserInterface};
7
8pub struct ConsoleUI;
9
10impl Default for ConsoleUI {
11 fn default() -> Self {
12 Self::new()
13 }
14}
15
16impl ConsoleUI {
17 pub fn new() -> Self {
18 Self
19 }
20
21 fn read_line(&self) -> Result<String> {
22 let mut input = String::new();
23 io::stdin()
24 .read_line(&mut input)
25 .context("Failed to read from stdin")?;
26 Ok(input.trim().to_string())
27 }
28}
29
30#[async_trait]
31impl UserInterface for ConsoleUI {
32 async fn prompt_input(&self, prompt: &str) -> Result<String> {
33 print!("{prompt}: ");
34 io::stdout().flush().context("Failed to flush stdout")?;
35 self.read_line()
36 }
37
38 async fn confirm(&self, message: &str) -> Result<bool> {
39 Confirm::with_theme(&ColorfulTheme::default())
40 .with_prompt(message)
41 .default(false)
42 .interact()
43 .context("Failed to get confirmation")
44 }
45
46 async fn select_from_list(&self, items: &[String], prompt: &str) -> Result<usize> {
47 if items.is_empty() {
48 return Err(anyhow::anyhow!("Cannot select from an empty list"));
49 }
50
51 let selection = Select::with_theme(&ColorfulTheme::default())
52 .with_prompt(prompt)
53 .items(items)
54 .default(0)
55 .interact_opt()
56 .context("Failed to select from list")?;
57
58 match selection {
59 Some(index) => Ok(index),
60 None => Err(ActrCliError::OperationCancelled.into()),
61 }
62 }
63
64 async fn display_service_table(
65 &self,
66 _items: &[ServiceInfo],
67 _headers: &[&str],
68 _formatter: fn(&ServiceInfo) -> Vec<String>,
69 ) {
70 }
72
73 async fn show_progress(&self, message: &str) -> Result<Box<dyn ProgressBar>> {
74 println!("⏳ {message}...");
75 Ok(Box::new(ConsoleProgressBar))
76 }
77}
78
79pub struct ConsoleProgressBar;
80
81impl ProgressBar for ConsoleProgressBar {
82 fn update(&self, _progress: f64) {}
83
84 fn set_message(&self, message: &str) {
85 println!("⏳ {message}...");
86 }
87
88 fn finish(&self) {}
89}