use espf::diag::EspfError;
use espf::parser::Parsed;
use espforge_model::Meta;
use espforge_options::{Answers, DerivedInputs, core_from_section, embedded_schema};
pub struct Frontend<'a> {
pub parsed: Parsed<'a>,
pub meta: Meta,
pub answers: Answers,
pub derived: DerivedInputs,
}
pub fn load(src: &str) -> Result<Frontend<'_>, Vec<EspfError>> {
let parsed = espf::parser::parse_espf(src);
let mut errors = espf::diag::junk_errors(&parsed.junk);
errors.extend(parsed.structure_errors.iter().cloned());
if !errors.is_empty() {
errors.sort_by_key(|e| e.span.start);
return Err(errors);
}
let header = parsed.header_offsets["espforge"];
let espforge_nodes = &parsed.sections["espforge"];
let (meta, opt_node) = core_from_section(header, espforge_nodes)?;
let answers = Answers::from_node(embedded_schema(), opt_node, header..header)?;
let derived = DerivedInputs::from_nodes(espforge_nodes, &parsed.sections["services"]);
Ok(Frontend {
parsed,
meta,
answers,
derived,
})
}
#[cfg(test)]
mod tests {
use super::*;
const OK: &str = "# espforge\n <project name=\"hello\" />\n <chip type=\"esp32c3\" />\n <runtime type=\"blocking\" />\n <options defaults=\"false\" />\n# peripherals\n# components\n# devices\n# services\n";
#[test]
fn loads_a_valid_file() {
let fe = load(OK).unwrap();
assert_eq!(fe.meta.chip, "esp32c3");
assert!(!fe.answers.defaults);
}
#[test]
fn missing_sections_are_reported_before_anything_else() {
let errs = load("# espforge\n <project name=\"h\" />\n")
.err()
.unwrap();
assert!(
errs.iter()
.any(|e| e.title.contains("missing section `# services`"))
);
}
#[test]
fn core_errors_are_reported() {
let src = OK.replace("esp32c3", "esp99");
let errs = load(&src).err().unwrap();
assert_eq!(errs[0].title, "unknown chip");
}
#[test]
fn option_errors_are_reported() {
let src = OK.replace("defaults=\"false\"", "wombat=\"1\"");
let errs = load(&src).err().unwrap();
assert!(errs[0].title.contains("unknown option `wombat`"));
}
}