espforge_lib/parse/
esp32.rs1use crate::parse::EspforgeConfiguration;
2use crate::parse::model::Esp32Config;
3use crate::parse::processor::{ProcessorRegistration, SectionProcessor};
4use anyhow::{Context, Result, bail};
5use serde_yaml_ng::Value;
6use std::collections::HashMap;
7
8pub struct PlatformProvisioner;
9
10impl SectionProcessor for PlatformProvisioner {
11 fn section_key(&self) -> &'static str {
12 "esp32"
13 }
14 fn priority(&self) -> u32 {
15 300
16 }
17
18 fn process(&self, content: &Value, model: &mut EspforgeConfiguration) -> Result<()> {
19 let esp32_config: Esp32Config = serde_yaml_ng::from_value(content.clone())
20 .context("Failed to deserialize esp32 configuration")?;
21
22 self.validate_pin_conflicts(&esp32_config)?;
24
25 model.esp32 = Some(esp32_config);
26 println!("✓ ESP32 platform provisioned");
27 Ok(())
28 }
29}
30
31impl PlatformProvisioner {
32 fn validate_pin_conflicts(&self, config: &Esp32Config) -> Result<()> {
33 let mut used_pins: HashMap<u8, Vec<String>> = HashMap::new();
34
35 for (name, gpio_cfg) in &config.gpio {
37 used_pins
38 .entry(gpio_cfg.pin)
39 .or_default()
40 .push(format!("gpio.{}", name));
41 }
42
43 for (name, spi_cfg) in &config.spi {
45 for (pin, pin_name) in [(spi_cfg.mosi, "mosi"), (spi_cfg.sck, "sck")] {
46 used_pins
47 .entry(pin)
48 .or_default()
49 .push(format!("spi.{}.{}", name, pin_name));
50 }
51 if let Some(miso) = spi_cfg.miso {
52 used_pins
53 .entry(miso)
54 .or_default()
55 .push(format!("spi.{}.miso", name));
56 }
57 }
58
59 for (name, i2c_cfg) in &config.i2c {
61 for (pin, pin_name) in [(i2c_cfg.sda, "sda"), (i2c_cfg.scl, "scl")] {
62 used_pins
63 .entry(pin)
64 .or_default()
65 .push(format!("i2c.{}.{}", name, pin_name));
66 }
67 }
68
69 for (name, uart_cfg) in &config.uart {
71 for (pin, pin_name) in [(uart_cfg.tx, "tx"), (uart_cfg.rx, "rx")] {
72 used_pins
73 .entry(pin)
74 .or_default()
75 .push(format!("uart.{}.{}", name, pin_name));
76 }
77 }
78
79 let conflicts: Vec<_> = used_pins
81 .iter()
82 .filter(|(_, usages)| usages.len() > 1)
83 .collect();
84
85 if !conflicts.is_empty() {
86 let error_msg = conflicts
87 .iter()
88 .map(|(pin, usages)| format!("Pin {} used by: {}", pin, usages.join(", ")))
89 .collect::<Vec<_>>()
90 .join("\n");
91 bail!("Pin conflicts detected:\n{}", error_msg);
92 }
93
94 Ok(())
95 }
96}
97
98inventory::submit! {
99 ProcessorRegistration {
100 factory: || Box::new(PlatformProvisioner),
101 }
102}