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_codegen::{dependency::DependencyKind, registry::find_plugin};
5use espforge_configuration::{components::Component, hardware::Esp32Config};
6use serde_yaml_ng::Value;
7use std::collections::HashMap;
8
9pub struct ComponentProvisioner;
10
11impl SectionProcessor for ComponentProvisioner {
12    fn section_key(&self) -> &'static str {
13        "components"
14    }
15
16    fn priority(&self) -> u32 {
17        200
18    }
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 _spec in components.values() {
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).ok_or_else(|| {
41                anyhow::anyhow!("Unknown driver '{}' for component '{}'", spec.driver, name)
42            })?;
43
44            // Plugin-specific validation
45            plugin
46                .validate(&spec.properties)
47                .with_context(|| format!("Validation failed for component '{}'", name))?;
48
49            // Resource existence validation
50            let deps = plugin.dependencies(&spec.properties)?;
51            for dep in deps {
52                if dep.kind == DependencyKind::Pin || dep.kind == DependencyKind::Peripheral {
53                    let res_name = dep.name.strip_prefix('$').unwrap_or(&dep.name);
54
55                    // Simple existence check for hardware resources
56                    if !Self::hardware_exists(esp32, res_name) {
57                        bail!(
58                            "Component '{}' references unknown hardware resource '{}'",
59                            name,
60                            res_name
61                        );
62                    }
63                }
64            }
65        }
66        Ok(())
67    }
68
69    fn hardware_exists(esp32: &Esp32Config, name: &str) -> bool {
70        esp32.gpio.contains_key(name)
71            || esp32.spi.contains_key(name)
72            || esp32.i2c.contains_key(name)
73            || esp32.uart.contains_key(name)
74    }
75}