Skip to main content

espforge_lib/parse/
components.rs

1use crate::parse::EspforgeConfiguration;
2use crate::parse::processor::SectionProcessor;
3use anyhow::{Context, Result, bail};
4use espforge_common::components::Component;
5use espforge_common::dependency::DependencyKind;
6use espforge_common::hardware::Esp32Config;
7use serde_yaml_ng::Value;
8use std::collections::HashMap;
9use espforge_common::codegen::find_plugin;
10
11pub struct ComponentProvisioner;
12
13impl SectionProcessor for ComponentProvisioner {
14    fn section_key(&self) -> &'static str {
15        "components"
16    }
17
18    fn priority(&self) -> u32 { 200 }
19
20    fn process(&self, content: &Value, model: &mut EspforgeConfiguration) -> Result<()> {
21        let components: HashMap<String, Component> = serde_yaml_ng::from_value(content.clone())
22            .context("Failed to deserialize components")?;
23
24        for (_name, _spec) in &components {
25            // Early validation using the plugin system if available
26            if let Some(esp32) = &model.esp32 {
27                self.validate(&components, esp32)?;
28            }
29        }
30        
31        model.components.extend(components);
32        println!("✓ {} components provisioned", model.components.len());
33        Ok(())
34    }
35}
36
37impl ComponentProvisioner {
38    fn validate(&self, components: &HashMap<String, Component>, esp32: &Esp32Config) -> Result<()> {
39         for (name, spec) in components {
40            let plugin = find_plugin(&spec.driver)
41                .ok_or_else(|| anyhow::anyhow!("Unknown driver '{}' for component '{}'", spec.driver, name))?;
42
43            // Plugin-specific validation
44            plugin.validate(&spec.properties)
45                .with_context(|| format!("Validation failed for component '{}'", name))?;
46
47            // Resource existence validation
48            let deps = plugin.dependencies(&spec.properties)?;
49            for dep in deps {
50                if dep.kind == DependencyKind::Pin || dep.kind == DependencyKind::Peripheral {
51                     let res_name = dep.name.strip_prefix('$').unwrap_or(&dep.name);
52                     
53                     // Simple existence check for hardware resources
54                     if !Self::hardware_exists(esp32, res_name) {
55                         bail!("Component '{}' references unknown hardware resource '{}'", name, res_name);
56                     }
57                }
58            }
59        }
60        Ok(())
61    }
62
63    fn hardware_exists(esp32: &Esp32Config, name: &str) -> bool {
64        esp32.gpio.contains_key(name) || esp32.spi.contains_key(name) ||
65        esp32.i2c.contains_key(name) || esp32.uart.contains_key(name)
66    }
67}