espforge_lib/parse/
esp32.rs

1use 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        // Validate pin assignments don't conflict
23        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        // Track GPIO pins
36        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        // Track SPI pins
44        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        // Track I2C pins
60        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        // Track UART pins
70        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        // Find conflicts
80        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}