Skip to main content

espforge_lib/parse/
mod.rs

1use crate::parse::processor::SectionProcessor;
2use anyhow::{Context, Result};
3use espforge_configuration::EspforgeConfiguration;
4use serde_yaml_ng::Value;
5
6pub mod components;
7pub mod devices;
8pub mod esp32;
9pub mod processor;
10pub mod project;
11
12pub struct ConfigParser {
13    processors: Vec<Box<dyn SectionProcessor>>,
14}
15
16impl Default for ConfigParser {
17    fn default() -> Self {
18        Self::new()
19    }
20}
21
22impl ConfigParser {
23    pub fn builder() -> ConfigParserBuilder {
24        ConfigParserBuilder::default()
25    }
26
27    pub fn new() -> Self {
28        ConfigParserBuilder::default_processors().build()
29    }
30
31    pub fn parse(&self, yaml_text: &str) -> Result<EspforgeConfiguration> {
32        let root_value: Value =
33            serde_yaml_ng::from_str(yaml_text).context("Failed to parse YAML")?;
34
35        let root_map = root_value
36            .as_mapping()
37            .ok_or_else(|| anyhow::anyhow!("Config must be a map"))?;
38
39        let mut model = EspforgeConfiguration::default();
40
41        for processor in &self.processors {
42            let key = processor.section_key();
43            // Value::String key lookup in serde_yaml_ng Mapping
44            if let Some(section_content) = root_map.get(Value::String(key.to_string())) {
45                processor
46                    .process(section_content, &mut model)
47                    .with_context(|| format!("Error processing configuration section '{}'", key))?;
48            }
49        }
50
51        // Validate chip existence by checking the map
52        if !model.espforge.contains_key("platform") && !model.espforge.contains_key("chip") {
53            return Err(anyhow::anyhow!(
54                "Config must specify a chip/platform (e.g., 'platform: esp32c3')"
55            ));
56        }
57
58        Ok(model)
59    }
60}
61
62#[derive(Default)]
63pub struct ConfigParserBuilder {
64    processors: Vec<Box<dyn SectionProcessor>>,
65}
66
67impl ConfigParserBuilder {
68    pub fn default_processors() -> Self {
69        Self::default()
70            .with_processor(Box::new(project::ProjectInfoProvisioner))
71            .with_processor(Box::new(esp32::PlatformProvisioner))
72            .with_processor(Box::new(components::ComponentProvisioner))
73            .with_processor(Box::new(devices::DeviceProvisioner))
74    }
75
76    pub fn with_processor(mut self, processor: Box<dyn SectionProcessor>) -> Self {
77        self.processors.push(processor);
78        self
79    }
80
81    pub fn build(mut self) -> ConfigParser {
82        self.processors
83            .sort_by_key(|p| std::cmp::Reverse(p.priority()));
84        ConfigParser {
85            processors: self.processors,
86        }
87    }
88}