Skip to main content

espforge_lib/cli/interactive/
dialoguer.rs

1use super::{Prompter, catalog::ExampleCatalog};
2use anyhow::{Result, bail};
3use dialoguer::{Input, Select, theme::ColorfulTheme};
4use espforge_dialogue::{Asker, EnumAsker};
5
6pub struct DialoguerPrompter {
7    theme: ColorfulTheme,
8}
9
10#[derive(Debug, Clone, EnumAsker)]
11#[asker(prompt = "Select Target Chip")]
12pub enum Chip {
13    #[asker(label = "esp32c3")]
14    Esp32c3,
15    #[asker(label = "esp32c6")]
16    Esp32c6,
17    #[asker(label = "esp32s3")]
18    Esp32s3,
19    #[asker(label = "esp32s2")]
20    Esp32s2,
21    #[asker(label = "esp32")]
22    Esp32,
23    #[asker(label = "esp32h2")]
24    Esp32h2,
25}
26
27impl std::fmt::Display for Chip {
28    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29        let s = match self {
30            Chip::Esp32c3 => "esp32c3",
31            Chip::Esp32c6 => "esp32c6",
32            Chip::Esp32s3 => "esp32s3",
33            Chip::Esp32s2 => "esp32s2",
34            Chip::Esp32 => "esp32",
35            Chip::Esp32h2 => "esp32h2",
36        };
37        write!(f, "{}", s)
38    }
39}
40
41#[derive(Asker)]
42struct OverwritePrompt {
43    #[confirm]
44    confirm: bool,
45}
46
47impl DialoguerPrompter {
48    pub fn new() -> Self {
49        Self {
50            theme: ColorfulTheme::default(),
51        }
52    }
53
54    pub fn select_from_list<T: ToString + std::fmt::Display>(
55        &self,
56        prompt: impl Into<String>,
57        items: &[T],
58    ) -> Result<usize> {
59        let selection = Select::with_theme(&self.theme)
60            .with_prompt(prompt.into())
61            .default(0)
62            .items(items)
63            .interact()?;
64        Ok(selection)
65    }
66
67    pub fn ensure_not_empty<T>(items: &[T], context: &str) -> Result<()> {
68        if items.is_empty() {
69            bail!("No {} found to select from", context);
70        }
71        Ok(())
72    }
73}
74
75impl Default for DialoguerPrompter {
76    fn default() -> Self {
77        Self::new()
78    }
79}
80
81impl Prompter for DialoguerPrompter {
82    fn select_example(&self) -> Result<String> {
83        let examples_by_category = ExampleCatalog::load();
84        let category = examples_by_category.select_category(self)?;
85        let example = examples_by_category.select_example_from_category(&category, self)?;
86        Ok(example)
87    }
88
89    fn prompt_project_name(&self, default: &str) -> Result<String> {
90        let input = Input::with_theme(&self.theme)
91            .with_prompt("Project Name (Destination Folder)")
92            .default(default.to_string())
93            .interact_text()?;
94        Ok(input)
95    }
96
97    fn select_chip(&self) -> Result<String> {
98        let chip = Chip::ask();
99        Ok(chip.to_string())
100    }
101
102    fn confirm_overwrite(&self, dir_name: &str) -> Result<bool> {
103        let prompt_text = format!("Directory '{}' already exists. Overwrite?", dir_name);
104        let prompt = OverwritePrompt::asker().confirm(prompt_text).finish();
105        Ok(prompt.confirm)
106    }
107}