espforge_lib/cli/interactive/
catalog.rs1use anyhow::Result;
2use std::collections::HashMap;
3
4use crate::cli::interactive::DialoguerPrompter;
5
6pub struct ExampleCatalog {
7 categories: HashMap<String, Vec<String>>,
8}
9
10impl ExampleCatalog {
11 pub fn load() -> Self {
12 use espforge_examples::EXAMPLES_DIR;
13
14 let categories = EXAMPLES_DIR
15 .dirs()
16 .filter_map(|entry| {
17 let category = Self::extract_name(entry.path().file_name());
18 let examples: Vec<String> = entry
19 .dirs()
20 .map(|ex| Self::extract_name(ex.path().file_name()))
21 .collect();
22
23 if examples.is_empty() {
24 None
25 } else {
26 Some((category, examples))
27 }
28 })
29 .collect();
30
31 Self { categories }
32 }
33
34 fn extract_name(file_name: Option<&std::ffi::OsStr>) -> String {
35 file_name.unwrap_or_default().to_string_lossy().to_string()
36 }
37
38 pub fn select_category(&self, prompter: &DialoguerPrompter) -> Result<String> {
39 let mut category_names: Vec<_> = self.categories.keys().cloned().collect();
40 category_names.sort();
41
42 DialoguerPrompter::ensure_not_empty(&category_names, "example categories")?;
43
44 let selection = prompter.select_from_list("Select a Category", &category_names)?;
45 Ok(category_names[selection].clone())
46 }
47
48 pub fn select_example_from_category(
49 &self,
50 category: &str,
51 prompter: &DialoguerPrompter,
52 ) -> Result<String> {
53 let examples = self
54 .categories
55 .get(category)
56 .ok_or_else(|| anyhow::anyhow!("Category '{}' not found", category))?;
57
58 DialoguerPrompter::ensure_not_empty(examples, &format!("examples in '{}'", category))?;
59
60 let selection =
61 prompter.select_from_list(format!("Select example from {}", category), examples)?;
62
63 Ok(examples[selection].clone())
64 }
65
66 pub fn categories(&self) -> Vec<&String> {
67 let mut cats: Vec<_> = self.categories.keys().collect();
68 cats.sort();
69 cats
70 }
71
72 pub fn examples_in_category(&self, category: &str) -> Option<&Vec<String>> {
73 self.categories.get(category)
74 }
75}
76
77pub struct ChipCatalog;
78
79impl ChipCatalog {
80 pub fn available_chips() -> Vec<&'static str> {
81 vec![
82 "esp32c3", "esp32c6", "esp32s3", "esp32s2", "esp32", "esp32h2",
83 ]
84 }
85
86 pub fn is_valid_chip(chip: &str) -> bool {
87 Self::available_chips().contains(&chip)
88 }
89}
90
91#[cfg(test)]
92mod tests {
93 use super::*;
94
95 #[test]
96 fn test_chip_validation() {
97 assert!(ChipCatalog::is_valid_chip("esp32c3"));
98 assert!(ChipCatalog::is_valid_chip("esp32"));
99 assert!(!ChipCatalog::is_valid_chip("esp32c9"));
100 assert!(!ChipCatalog::is_valid_chip("invalid"));
101 }
102}