espforge_lib/nibblers/
components.rs

1use crate::{
2    config::EspforgeConfiguration,
3    nibblers::{ConfigNibbler, NibblerResult, NibblerStatus},
4    register_nibbler,
5};
6use espforge_macros::auto_register_nibbler;
7use serde_yaml_ng::Value;
8
9#[derive(Default)]
10#[auto_register_nibbler]
11pub struct ComponentNibbler;
12
13impl ConfigNibbler for ComponentNibbler {
14    fn name(&self) -> &str {
15        "ComponentNibbler"
16    }
17
18    fn priority(&self) -> u8 {
19        20
20    }
21
22    fn process(&self, config: &EspforgeConfiguration) -> Result<NibblerResult, String> {
23        let components = match &config.components {
24            Some(comps) => comps,
25            None => return Ok(self.empty_result()),
26        };
27
28        let (findings, status) = self.validate_components(components, config);
29
30        Ok(NibblerResult {
31            nibbler_name: self.name().to_string(),
32            findings,
33            status,
34        })
35    }
36}
37
38impl ComponentNibbler {
39    fn empty_result(&self) -> NibblerResult {
40        NibblerResult {
41            nibbler_name: self.name().to_string(),
42            findings: Vec::new(),
43            status: NibblerStatus::Ok,
44        }
45    }
46
47    fn validate_components(
48        &self,
49        components: &std::collections::HashMap<String, crate::config::ComponentConfig>,
50        config: &EspforgeConfiguration,
51    ) -> (Vec<String>, NibblerStatus) {
52        let mut findings = Vec::new();
53        let mut has_errors = false;
54
55        for (comp_name, comp_config) in components {
56            findings.push(format!(
57                "Checking component '{}' (using {})",
58                comp_name, comp_config.using
59            ));
60
61            has_errors |=
62                self.validate_component_references(comp_name, comp_config, config, &mut findings);
63        }
64
65        let status = if has_errors {
66            NibblerStatus::Error
67        } else {
68            NibblerStatus::Ok
69        };
70
71        (findings, status)
72    }
73
74    fn validate_component_references(
75        &self,
76        comp_name: &str,
77        comp_config: &crate::config::ComponentConfig,
78        config: &EspforgeConfiguration,
79        findings: &mut Vec<String>,
80    ) -> bool {
81        let mut has_errors = false;
82
83        for (key, value) in &comp_config.with {
84            if let Some(ref_name) = self.extract_reference(value) {
85                if self.is_valid_reference(ref_name, config) {
86                    findings.push(format!("  Validated reference '${}'", ref_name));
87                } else {
88                    findings.push(format!(
89                        "  Error: Component '{}' references undefined hardware resource '${}' in property '{}'",
90                        comp_name, ref_name, key
91                    ));
92                    has_errors = true;
93                }
94            }
95        }
96
97        has_errors
98    }
99
100    fn extract_reference<'a>(&self, value: &'a Value) -> Option<&'a str> {
101        match value {
102            Value::String(s) => s.strip_prefix('$'),
103            _ => None,
104        }
105    }
106
107    fn is_valid_reference(&self, ref_name: &str, config: &EspforgeConfiguration) -> bool {
108        config
109            .esp32
110            .as_ref()
111            .map(|esp32| {
112                esp32.gpio.contains_key(ref_name)
113                    || esp32.spi.contains_key(ref_name)
114                    || esp32.i2c.contains_key(ref_name)
115                    || esp32.uart.contains_key(ref_name)
116            })
117            .unwrap_or(false)
118    }
119}