extern crate pest;
#[macro_use]
extern crate pest_derive;
use pest::Parser;
use std::collections::HashMap;
use std::fs;
pub type INIParserResult<T> = Result<T, InIParseError>;
#[derive(Debug)]
pub enum InIParseError {
FileReadError(String),
UnsuccessfulParse(String),
Finished,
Unreachable,
}
#[derive(Parser)]
#[grammar = "ini.pest"]
pub struct Ini;
#[derive(Debug)]
pub struct INIParser {
pub output: HashMap<String, HashMap<String, String>>,
}
impl INIParser {
pub fn from_string(content: &str) -> INIParserResult<Self> {
Self::parse(content)
}
pub fn from_file(path: &str) -> INIParserResult<Self> {
let content = fs::read_to_string(path)
.map_err(|err| InIParseError::FileReadError(err.to_string()))?;
Self::parse(&content)
}
pub fn into_inner(self) -> HashMap<String, HashMap<String, String>> {
self.output
}
fn parse(content: &str) -> INIParserResult<Self> {
let ini = Ini::parse(Rule::file, content)
.map_err(|err| InIParseError::UnsuccessfulParse(err.to_string()))?
.next()
.ok_or(InIParseError::UnsuccessfulParse(
"Unsuccessful parse".to_string(),
))?;
let mut output: HashMap<String, HashMap<String, String>> = HashMap::new();
let mut current_section = "untagged".to_string();
for line in ini.into_inner() {
match line.as_rule() {
Rule::section => {
current_section = line.into_inner()
.next()
.ok_or(InIParseError::Finished)?
.as_str()
.to_string();
}
Rule::property => {
let mut prop = line.into_inner();
let name = prop
.next()
.ok_or(InIParseError::Finished)?
.as_str()
.to_string();
let val = prop
.next()
.ok_or(InIParseError::Finished)?
.as_str()
.to_string();
output.entry(current_section.to_string())
.or_default()
.insert(name, val);
}
Rule::EOI => (),
_ => unreachable!(),
};
}
Ok(Self { output })
}
}