espforge_lib/parse/
components.rs

1use crate::parse::EspforgeConfiguration;
2use crate::parse::model::{Component, ComponentResource, Esp32Config};
3use crate::parse::processor::{ProcessorRegistration, SectionProcessor};
4use anyhow::{Context, Result, bail};
5use serde_yaml_ng::Value;
6use std::collections::HashMap;
7
8pub struct ComponentProvisioner;
9
10impl SectionProcessor for ComponentProvisioner {
11    fn section_key(&self) -> &'static str {
12        "components"
13    }
14    fn priority(&self) -> u32 {
15        200
16    } // Process after ESP32
17
18    fn process(&self, content: &Value, model: &mut EspforgeConfiguration) -> Result<()> {
19        let components: HashMap<String, Component> = serde_yaml_ng::from_value(content.clone())
20            .context("Failed to deserialize components")?;
21
22        // Validate all resource references
23        if let Some(esp32) = &model.esp32 {
24            self.validate_references(&components, esp32)?;
25        } else {
26            bail!("Components require esp32 section to be processed first");
27        }
28
29        model.components = components;
30        println!("✓ {} components provisioned", model.components.len());
31        Ok(())
32    }
33}
34
35impl ComponentProvisioner {
36    fn validate_references(
37        &self,
38        components: &HashMap<String, Component>,
39        esp32: &Esp32Config,
40    ) -> Result<()> {
41        let errors: Vec<_> = components
42            .iter()
43            .flat_map(|(name, component)| {
44                component
45                    .resource_refs()
46                    .filter_map(|res_ref| {
47                        res_ref
48                            .reference
49                            .strip_prefix('$')
50                            .filter(|res_name| {
51                                !Self::resource_exists(esp32, res_ref.resource_type, res_name)
52                            })
53                            .map(|_| {
54                                format!(
55                                    "Component '{}': {} reference '{}' not found",
56                                    name, res_ref.resource_type, res_ref.reference
57                                )
58                            })
59                    })
60                    .collect::<Vec<_>>()
61            })
62            .collect();
63
64        if !errors.is_empty() {
65            bail!("Component validation failed:\n{}", errors.join("\n"));
66        }
67
68        Ok(())
69    }
70
71    fn resource_exists(esp32: &Esp32Config, res_type: &str, name: &str) -> bool {
72        match res_type {
73            "gpio" => esp32.gpio.contains_key(name),
74            "spi" => esp32.spi.contains_key(name),
75            "i2c" => esp32.i2c.contains_key(name),
76            "uart" => esp32.uart.contains_key(name),
77            _ => false,
78        }
79    }
80}
81
82inventory::submit! {
83    ProcessorRegistration {
84        factory: || Box::new(ComponentProvisioner),
85    }
86}