pub mod macros; use pest::Parser; use pest_derive::Parser; use std::collections::HashMap; use anyhow::{Result}; #[derive(Parser)] #[grammar = "specfile.pest"] struct SpecFileParser; #[derive(Default, Debug)] pub struct SpecFile { pub name: String, pub version: String, pub release: String, pub summary: String, pub license: String, pub sources: Vec, pub variables: HashMap, pub description: String, pub prep_script: String, pub build_script: String, pub install_script: String, pub files: Vec, pub changelog: String, } enum KnownVariableControl { Name, Version, Release, Summary, License, None, } fn append_newline_string(s: &str, section_line: i32) -> String { if section_line == 0 { return s.to_owned(); } return "\n".to_owned() + s; } pub fn parse(file_contents: String) -> Result { let pairs = SpecFileParser::parse(Rule::file, &file_contents)?; let mut spec = SpecFile::default(); for pair in pairs { // A pair can be converted to an iterator of the tokens which make it up: match pair.as_rule() { Rule::variable => { let mut var_control = KnownVariableControl::None; let mut var_name_tmp = String::new(); for variable_rule in pair.clone().into_inner() { match variable_rule.as_rule() { Rule::variable_name => { match variable_rule.as_str() { "Name" => var_control = KnownVariableControl::Name, "Version" => var_control = KnownVariableControl::Version, "Release" => var_control = KnownVariableControl::Release, "Summary" => var_control = KnownVariableControl::Summary, "License" => var_control = KnownVariableControl::License, _ => var_control = { var_name_tmp = variable_rule.as_str().to_string(); KnownVariableControl::None }, } } Rule::variable_text => { match var_control { KnownVariableControl::Name => spec.name = variable_rule.as_str().to_string(), KnownVariableControl::Version => spec.version = variable_rule.as_str().to_string(), KnownVariableControl::Release => spec.release = variable_rule.as_str().to_string(), KnownVariableControl::Summary =>spec.summary = variable_rule.as_str().to_string(), KnownVariableControl::License => spec.license = variable_rule.as_str().to_string(), KnownVariableControl::None => { spec.variables.insert(var_name_tmp.clone(), variable_rule.as_str().to_string()); } } } _ => () } } } Rule::section => { let mut section_name_tmp = String::new(); let mut section_line = 0; for section_rule in pair.clone().into_inner() { match section_rule.as_rule() { Rule::section_name => { section_name_tmp = section_rule.as_str().to_string() } Rule::section_line => { for line_or_comment in section_rule.into_inner() { match line_or_comment.as_rule() { Rule::section_text => { match section_name_tmp.as_str() { "description" => { spec.description.push_str(append_newline_string(line_or_comment.as_str(), section_line).as_str()); section_line = section_line + 1 }, "prep" => { spec.prep_script.push_str(append_newline_string(line_or_comment.as_str(), section_line).as_str()); section_line = section_line + 1 }, "build" => { spec.build_script.push_str(append_newline_string(line_or_comment.as_str(), section_line).as_str()); section_line = section_line + 1 }, "files" => spec.files.push(line_or_comment.as_str().trim_end().to_string()), "install" => { spec.install_script.push_str(append_newline_string(line_or_comment.as_str(), section_line).as_str()); section_line = section_line + 1 }, "changelog" => { spec.changelog.push_str(append_newline_string(line_or_comment.as_str(), section_line).as_str()); section_line = section_line + 1 }, _ => panic!( "Unknown Section: {:?}", line_or_comment.as_rule() ), } } _ => () } } } _ => panic!("Rule not known please update the code: {:?}", section_rule.as_rule()), } } } Rule::EOI => (), _ => panic!("Rule not known please update the code: {:?}", pair.as_rule()), } } Ok(spec) } #[cfg(test)] mod tests { use std::fs; use crate::parse; #[test] fn it_works() { assert_eq!(2 + 2, 4); } #[test] fn test_parse() { let contents = fs::read_to_string("src/test_data/simple.spec"); match contents { Ok(file) => { let spec = parse(file); assert!(spec.is_ok(), "parsing error {:?}", spec) }, Err(e) => panic!("io error: {:}", e) } } }