espforge_lib/parse/
components.rs1use 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 } 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 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}