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